81 Commits

Author SHA1 Message Date
gitea-actions[bot] 28e61b8f8a chore(version): pre-release bump to 02.08.04 [skip ci] 2026-05-26 07:06:54 +00:00
jmiller 1f0b4596ff sync: update-server.yml with updates.xml integrity check [skip ci] 2026-05-26 04:47:46 +00:00
jmiller dac39212d7 Merge pull request 'chore: cascade main → dev (43abc65) [skip ci]' (#45) from main into dev
chore: cascade main → dev [skip ci]
2026-05-26 04:37:41 +00:00
jmiller 43abc6514e Merge pull request 'Release 02.09.00: CI fixes, update server standard, Joomla skill' (#44) from dev into main
Universal: Cascade Main → Dev / Cascade main → branches (push) Successful in 3s
Joomla: Repo Health / Release configuration (push) Blocked by required conditions
Joomla: Repo Health / Scripts governance (push) Blocked by required conditions
Joomla: Repo Health / Repository health (push) Blocked by required conditions
Joomla: Repo Health / Access control (push) Successful in 1s
2026-05-26 04:37:36 +00:00
Jonathan Miller 8d42ef40c5 Merge remote-tracking branch 'origin/main' into dev
Joomla: Repo Health / Release configuration (push) Blocked by required conditions
Joomla: Repo Health / Scripts governance (push) Blocked by required conditions
Joomla: Repo Health / Repository health (push) Blocked by required conditions
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Joomla: Repo Health / Release configuration (pull_request) Blocked by required conditions
Joomla: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Joomla: Repo Health / Repository health (pull_request) Blocked by required conditions
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Joomla: Repo Health / Access control (pull_request) Successful in 1s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 4s
Universal: PR Check / Validate PR (pull_request) Failing after 3s
Joomla: Extension CI / Release Readiness Check (pull_request) Successful in 4s
Universal: PR Check / Changelog Updated (pull_request) Successful in 3s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 5s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 11s
# Conflicts:
#	.mokogitea/workflows/auto-release.yml
#	.mokogitea/workflows/pre-release.yml
#	README.md
#	src/packages/com_mokowaas/mokowaas.xml
#	src/packages/plg_system_mokowaas/mokowaas.xml
#	src/packages/plg_webservices_mokowaas/mokowaas.xml
#	src/pkg_mokowaas.xml
#	updates.xml
2026-05-25 23:37:11 -05:00
gitea-actions[bot] 0546dde89f chore(version): pre-release bump to 02.08.03 [skip ci] 2026-05-26 04:00:46 +00:00
jmiller 598ec0712c fix: updates.xml tag dev + client site [skip ci] 2026-05-26 03:52:18 +00:00
Jonathan Miller 6f9df77f79 fix(ci): remove dead XML_TAG code, CLI handles tag mapping
Joomla: Repo Health / Release configuration (push) Blocked by required conditions
Joomla: Repo Health / Scripts governance (push) Blocked by required conditions
Joomla: Repo Health / Repository health (push) Blocked by required conditions
Joomla: Repo Health / Access control (push) Successful in 3s
The updates_xml_build.php CLI now maps 'development' → 'dev' internally.
Removed the dead shell case block that was setting XML_TAG (unused since
the inline PHP updater was replaced with the CLI).

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 22:51:19 -05:00
jmiller 89aaef14e7 fix: updates.xml same version all channels + client tag [skip ci] 2026-05-26 03:33:21 +00:00
gitea-actions[bot] f72cafe4d7 chore(version): pre-release bump to 02.08.02 [skip ci] 2026-05-26 03:26:36 +00:00
Jonathan Miller a965bcf0ef refactor(ci): clean up auto-release, move logic to CLI tools
Joomla: Repo Health / Release configuration (push) Blocked by required conditions
Joomla: Repo Health / Scripts governance (push) Blocked by required conditions
Joomla: Repo Health / Repository health (push) Blocked by required conditions
Joomla: Repo Health / Access control (push) Successful in 1s
- Remove duplicate asset deletion loop (new loop handles all assets)
- Remove inline Python updates.xml updater (Step 5 CLI handles it)
- Remove dead Step 8 sync-to-main code (Step 5 commit+push handles it)
- Step 8b uses release_body_update.php CLI with fallback
- Step 6 tag creation always runs (removed never-set is_minor gate)
- Net: -154 lines, +25 lines

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 22:20:28 -05:00
Jonathan Miller bd6eec88af feat(ci): checksums as [filename].sha256 assets, not in release body
Joomla: Repo Health / Release configuration (push) Blocked by required conditions
Joomla: Repo Health / Scripts governance (push) Blocked by required conditions
Joomla: Repo Health / Repository health (push) Blocked by required conditions
Joomla: Repo Health / Access control (push) Successful in 4s
Upload per-file .sha256 checksum files alongside packages instead of
embedding SHA-256 in the release description. Format: "hash  filename"
matching standard sha256sum output.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 22:11:53 -05:00
Jonathan Miller ce7e36f779 feat(ci): add version_check.php to pre-release and auto-release workflows
Joomla: Repo Health / Release configuration (push) Blocked by required conditions
Joomla: Repo Health / Scripts governance (push) Blocked by required conditions
Joomla: Repo Health / Repository health (push) Blocked by required conditions
Joomla: Repo Health / Access control (push) Successful in 1s
Runs --fix mode after version bump to auto-correct any drift between
README.md and manifest XML files before building.

Refs: moko-platform#122

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 22:08:40 -05:00
jmiller 46b1469121 chore: sync .mokogitea/workflows/auto-release.yml from moko-platform [skip ci] 2026-05-26 03:07:18 +00:00
jmiller 1e936a67c4 chore: sync .mokogitea/workflows/pre-release.yml from moko-platform [skip ci] 2026-05-26 03:05:25 +00:00
jmiller 0903a4b335 fix: sync updates.xml with all channels and correct versions [skip ci] 2026-05-26 03:05:10 +00:00
gitea-actions[bot] a7823c6440 chore: update updates.xml (development: 02.08.01-dev) [skip ci] 2026-05-26 03:04:40 +00:00
jmiller ed720b2ea9 chore: sync updates.xml from [skip ci] 2026-05-26 03:04:40 +00:00
gitea-actions[bot] 263ac78515 chore(version): auto-bump patch 02.08.01 [skip ci] 2026-05-26 03:04:39 +00:00
Jonathan Miller b9f83c43bc chore: reset versions to 02.08.00, sync all update streams
Joomla: Repo Health / Release configuration (push) Blocked by required conditions
Joomla: Repo Health / Scripts governance (push) Blocked by required conditions
Joomla: Repo Health / Repository health (push) Blocked by required conditions
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Update Server / Update updates.xml (push) Successful in 9s
Dev is now ahead of main (02.07.00). All manifests, README, and
updates.xml aligned. Updates.xml has all 6 channels with correct
pkg_mokowaas element.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 22:04:25 -05:00
Jonathan Miller f4609088e3 fix(ci): auto-release preserves all update channels [skip ci]
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 21:56:10 -05:00
Jonathan Miller d9326ea34b fix(ci): auto-release fetches updates.xml from main before building
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Repo Health / Release configuration (push) Failing after 8s
Joomla: Repo Health / Repository health (push) Failing after 9s
Joomla: Repo Health / Scripts governance (push) Successful in 12s
Preserves all existing channel entries (dev, alpha, beta, rc) when
adding/updating the stable entry. Previously the file on disk was
empty or stale, so the preserve logic had nothing to keep.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 21:54:42 -05:00
jmiller 6589adcf75 fix: restore all channel entries in updates.xml [skip ci] 2026-05-26 02:51:58 +00:00
jmiller 2e2c1b82b3 chore: sync updates.xml 02.07.00 [skip ci] 2026-05-26 02:50:57 +00:00
gitea-actions[bot] 0451fa2138 chore(version): pre-release bump to 02.06.04 [skip ci] 2026-05-26 02:50:52 +00:00
jmiller 66b90754f8 Merge pull request 'chore: cascade main → dev (e66b7e9) [skip ci]' (#43) from main into dev
chore: cascade main → dev [skip ci]
2026-05-26 02:50:37 +00:00
jmiller e66b7e9a79 Merge pull request 'Release 02.07.00: Update site fix, settings protection, master god privs' (#42) from dev into main
Joomla: Repo Health / Release configuration (push) Blocked by required conditions
Joomla: Repo Health / Scripts governance (push) Blocked by required conditions
Joomla: Repo Health / Repository health (push) Blocked by required conditions
Joomla: Repo Health / Access control (push) Successful in 3s
Universal: Cascade Main → Dev / Cascade main → branches (push) Successful in 6s
2026-05-26 02:49:08 +00:00
Jonathan Miller 4f056763e9 Merge remote-tracking branch 'origin/main' into dev
Joomla: Repo Health / Release configuration (pull_request) Blocked by required conditions
Joomla: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Joomla: Repo Health / Repository health (pull_request) Blocked by required conditions
Joomla: Repo Health / Access control (push) Successful in 2s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 6s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been skipped
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been skipped
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 9s
Joomla: Repo Health / Access control (pull_request) Successful in 3s
Universal: PR Check / Validate PR (pull_request) Failing after 9s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 15s
Universal: PR Check / Changelog Updated (pull_request) Successful in 10s
Universal: PR Check / Build RC Package (pull_request) Has been skipped
Joomla: Update Server / Update updates.xml (push) Failing after 25s
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been skipped
Joomla: Repo Health / Release configuration (push) Failing after 6s
Joomla: Repo Health / Scripts governance (push) Successful in 5s
Joomla: Repo Health / Repository health (push) Failing after 5s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 24s
# Conflicts:
#	README.md
#	updates.xml
2026-05-25 21:48:49 -05:00
jmiller de70224728 fix: updates.xml all entries use pkg_mokowaas element [skip ci] 2026-05-26 02:36:40 +00:00
gitea-actions[bot] 6f69af666f chore: update updates.xml (development: 02.05.05-dev) [skip ci] 2026-05-26 02:34:05 +00:00
jmiller 1f7278022c chore: sync updates.xml from [skip ci] 2026-05-26 02:34:05 +00:00
gitea-actions[bot] b5e8d3dfe2 chore(version): auto-bump patch 02.05.05 [skip ci] 2026-05-26 02:34:04 +00:00
Jonathan Miller 3edec0687c fix: re-enable update site disabled by Joomla's protected flag
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Update Server / Update updates.xml (push) Successful in 8s
Joomla: Repo Health / Release configuration (push) Failing after 8s
Joomla: Repo Health / Scripts governance (push) Successful in 8s
Joomla: Repo Health / Repository health (push) Failing after 6s
Joomla automatically disables update sites for extensions with
protected=1. ensureProtectedFlag() now also checks and re-enables
the MokoWaaS update site each admin session.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 21:33:53 -05:00
jmiller a503e12ef9 fix: updates.xml with all channels (stable, rc, beta, alpha, dev, legacy) [skip ci] 2026-05-26 02:20:26 +00:00
Jonathan Miller ea60ac60ba fix(ci): pre-release uses updates_xml_build CLI with preserve logic
Joomla: Repo Health / Release configuration (push) Blocked by required conditions
Joomla: Repo Health / Scripts governance (push) Blocked by required conditions
Joomla: Repo Health / Repository health (push) Blocked by required conditions
Joomla: Repo Health / Access control (push) Successful in 1s
The inline PHP regex approach only updated existing entries — if no
matching channel entry existed, nothing was added. Now uses the
moko-platform CLI which creates new entries and preserves other channels.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 21:18:22 -05:00
jmiller 825820f7b9 fix: add RC and legacy entries to updates.xml [skip ci] 2026-05-26 02:17:42 +00:00
gitea-actions[bot] ba4a806cd7 chore(version): pre-release bump to 02.06.03 [skip ci] 2026-05-26 02:09:25 +00:00
Jonathan Miller effd1fd588 fix(ci): use absolute paths in package build step [skip ci]
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 21:09:09 -05:00
Jonathan Miller bf2b01df2d fix(ci): use absolute paths in package build step
Joomla: Repo Health / Release configuration (push) Blocked by required conditions
Joomla: Repo Health / Scripts governance (push) Blocked by required conditions
Joomla: Repo Health / Repository health (push) Blocked by required conditions
Joomla: Repo Health / Access control (push) Successful in 1s
Relative path ../../build/package/ broke when cd'd 3 levels deep into
src/packages/ext_name/. Now captures REPO_ROOT=$(pwd) and uses it
for zip output and cd back.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 21:08:41 -05:00
gitea-actions[bot] 4581088a0a chore(version): pre-release bump to 02.06.02 [skip ci] 2026-05-26 02:05:10 +00:00
gitea-actions[bot] 863dbb02f4 chore(version): pre-release bump to 02.06.01 [skip ci] 2026-05-26 02:01:47 +00:00
jmiller 8fd8015b19 chore: sync updates.xml 02.06.00 [skip ci] 2026-05-26 01:58:56 +00:00
jmiller 83ddbf0d73 Merge pull request 'chore: cascade main → dev (fad0170) [skip ci]' (#41) from main into dev
chore: cascade main → dev [skip ci]
2026-05-26 01:58:45 +00:00
jmiller fad0170cef Merge pull request 'Release 02.07.00-rc: Master god privs, settings protection, install API fix' (#40) from dev into main
Universal: Cascade Main → Dev / Cascade main → branches (push) Successful in 6s
Joomla: Repo Health / Release configuration (push) Blocked by required conditions
Joomla: Repo Health / Scripts governance (push) Blocked by required conditions
Joomla: Repo Health / Repository health (push) Blocked by required conditions
Joomla: Repo Health / Access control (push) Successful in 2s
2026-05-26 01:58:39 +00:00
jmiller a734d381ac chore: sync updates.xml from [skip ci] 2026-05-26 01:55:08 +00:00
gitea-actions[bot] 0b8f492613 chore: update updates.xml (development: 02.05.04-dev) [skip ci]
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 18s
2026-05-26 01:55:07 +00:00
gitea-actions[bot] 11c3488438 chore(version): auto-bump patch 02.05.04 [skip ci] 2026-05-26 01:55:06 +00:00
Jonathan Miller cc709a0231 security: master user bypasses all tenant restrictions
Joomla: Repo Health / Release configuration (push) Blocked by required conditions
Joomla: Repo Health / Scripts governance (push) Blocked by required conditions
Joomla: Repo Health / Repository health (push) Blocked by required conditions
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Update Server / Update updates.xml (push) Successful in 8s
Moved isMasterUser() check to top of enforceAdminRestrictions() so
master user is never blocked by any restriction including install
from URL, global config, sysinfo, installer, and template editing.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 20:54:58 -05:00
jmiller 0a0d998208 chore: sync updates.xml from [skip ci] 2026-05-26 01:38:26 +00:00
gitea-actions[bot] 03839601bb chore: update updates.xml (development: 02.05.03-dev) [skip ci] 2026-05-26 01:38:25 +00:00
gitea-actions[bot] 3e28dd4fae chore(version): auto-bump patch 02.05.03 [skip ci] 2026-05-26 01:38:24 +00:00
Jonathan Miller 2674111e0b security: block non-master users from editing MokoWaaS settings
Joomla: Repo Health / Access control (push) Successful in 2s
Joomla: Update Server / Update updates.xml (push) Successful in 8s
Joomla: Repo Health / Release configuration (push) Failing after 4s
Joomla: Repo Health / Scripts governance (push) Successful in 4s
Joomla: Repo Health / Repository health (push) Failing after 4s
Non-master users navigating to the plugin edit page are redirected
back to the plugins list with a warning message.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 20:38:14 -05:00
gitea-actions[bot] 7488225aa6 chore(version): pre-release bump to 02.05.02 [skip ci] 2026-05-26 01:30:19 +00:00
jmiller c1a9816c57 fix: updates.xml element=pkg_mokowaas + legacy plugin entry [skip ci] 2026-05-26 01:25:32 +00:00
jmiller 2d1932719a chore: sync updates.xml 02.06.00 [skip ci] 2026-05-26 01:12:15 +00:00
jmiller 315be81e20 Merge pull request 'chore: cascade main → dev (65d9aa3) [skip ci]' (#39) from main into dev
chore: cascade main → dev [skip ci]
2026-05-26 01:12:07 +00:00
jmiller 65d9aa3e9f Merge pull request 'Release 02.06.00: Alias offline bypass, install API fix, stream tags' (#38) from dev into main
Universal: Cascade Main → Dev / Cascade main → branches (push) Successful in 3s
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Repo Health / Release configuration (push) Failing after 3s
Joomla: Repo Health / Scripts governance (push) Successful in 4s
Joomla: Repo Health / Repository health (push) Failing after 4s
2026-05-26 01:12:02 +00:00
jmiller 8243e8c49d chore: sync updates.xml from [skip ci] 2026-05-26 01:11:51 +00:00
gitea-actions[bot] c9d31b3ba4 chore: update updates.xml (development: 02.05.01-dev) [skip ci]
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 13s
2026-05-26 01:11:50 +00:00
gitea-actions[bot] 29cfee7154 chore(version): auto-bump patch 02.05.01 [skip ci] 2026-05-26 01:11:49 +00:00
Jonathan Miller bbae842fdb Merge remote-tracking branch 'origin/main' into dev
Joomla: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Joomla: Repo Health / Access control (pull_request) Successful in 1s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 4s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 5s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 5s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been skipped
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been skipped
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been skipped
Universal: PR Check / Changelog Updated (pull_request) Successful in 5s
Universal: PR Check / Validate PR (pull_request) Failing after 4s
Universal: PR Check / Build RC Package (pull_request) Has been skipped
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been skipped
Joomla: Update Server / Update updates.xml (push) Successful in 10s
Joomla: Repo Health / Release configuration (push) Failing after 4s
Joomla: Repo Health / Scripts governance (push) Successful in 4s
Joomla: Repo Health / Repository health (push) Failing after 4s
Joomla: Repo Health / Release configuration (pull_request) Failing after 4s
Joomla: Repo Health / Scripts governance (pull_request) Successful in 4s
Joomla: Repo Health / Repository health (pull_request) Failing after 5s
# Conflicts:
#	README.md
2026-05-25 20:11:38 -05:00
jmiller 85e966a3f4 chore: sync updates.xml from [skip ci] 2026-05-26 01:04:00 +00:00
gitea-actions[bot] 3d8bfb6112 chore: update updates.xml (development: 02.04.02-dev) [skip ci] 2026-05-26 01:03:59 +00:00
gitea-actions[bot] 7822064045 chore(version): auto-bump patch 02.04.02 [skip ci] 2026-05-26 01:03:59 +00:00
Jonathan Miller 906861638f fix: install API extracts ZIP before passing to Joomla Installer
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Update Server / Update updates.xml (push) Successful in 8s
Joomla: Repo Health / Release configuration (push) Failing after 5s
Joomla: Repo Health / Scripts governance (push) Successful in 5s
Joomla: Repo Health / Repository health (push) Failing after 6s
Installer::install() expects a directory path, not a ZIP file. Now
downloads → extracts to tmp dir → installs from extracted dir → cleans up.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 20:03:47 -05:00
Jonathan Miller 78dd453a9b chore: promote CHANGELOG for 02.06.00 release
Joomla: Repo Health / Access control (push) Successful in 3s
Joomla: Repo Health / Release configuration (push) Failing after 4s
Joomla: Repo Health / Scripts governance (push) Successful in 4s
Joomla: Repo Health / Repository health (push) Failing after 5s
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 20:01:14 -05:00
jmiller 204520d9c9 chore: sync updates.xml from [skip ci] 2026-05-26 00:59:11 +00:00
gitea-actions[bot] 72b967c0ab chore: update updates.xml (development: 02.04.01-dev) [skip ci] 2026-05-26 00:59:10 +00:00
gitea-actions[bot] 781266885f chore(version): auto-bump patch 02.04.01 [skip ci] 2026-05-26 00:59:09 +00:00
Jonathan Miller a869619fcd feat: alias offline=No bypasses Joomla global offline setting
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Update Server / Update updates.xml (push) Successful in 9s
Joomla: Repo Health / Release configuration (push) Failing after 3s
Joomla: Repo Health / Scripts governance (push) Successful in 4s
Joomla: Repo Health / Repository health (push) Failing after 4s
When an alias domain has offline=No, the plugin overrides Joomla's
configuration.php offline=1 setting. This allows the dev/staging alias
to remain accessible while the main site shows the offline page.

Use case: put clarksvillefurs.com offline for maintenance while
clarksvillefurs.dev.mokoconsulting.tech stays accessible for testing.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 19:58:52 -05:00
Jonathan Miller 625965e129 fix(ci): use stream tag 'stable' instead of version tag 'vXX'
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Repo Health / Release configuration (push) Failing after 3s
Joomla: Repo Health / Scripts governance (push) Successful in 4s
Joomla: Repo Health / Repository health (push) Failing after 4s
Release tags should use update stream names (stable, development,
release-candidate) not version-based tags (v02, v03). This aligns
with the pre-release workflow which already uses stream tags.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 19:22:05 -05:00
jmiller 91504c663b fix: use stream tag 'stable' not version tag in updates.xml [skip ci] 2026-05-26 00:21:50 +00:00
jmiller 6cd690b737 feat(ci): add issue-branch.yml [skip ci] 2026-05-25 05:12:32 +00:00
Jonathan Miller 3b2fe37ce1 chore: update CHANGELOG for 02.05.00 stable release
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Repo Health / Release configuration (push) Failing after 3s
Joomla: Repo Health / Scripts governance (push) Successful in 3s
Joomla: Repo Health / Repository health (push) Failing after 4s
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-24 23:15:29 -05:00
jmiller 8fea27e8b6 fix(ci): update pre-release.yml - PHP CLI tools, fix broken platform detection [skip ci] 2026-05-25 04:12:59 +00:00
jmiller cbea5752d1 fix: correct updates.xml stable entries [skip ci] 2026-05-25 04:07:11 +00:00
jmiller 34e789298b chore: sync updates.xml 02.05.00 [skip ci] 2026-05-25 03:59:36 +00:00
gitea-actions[bot] 62c49eab5a chore(release): ZIP + tar.gz for 02.05.00 [skip ci] 2026-05-25 03:59:35 +00:00
gitea-actions[bot] 2f8c81792d chore(release): build 02.05.00 [skip ci] 2026-05-25 03:59:33 +00:00
jmiller 9a356cdd04 Merge pull request 'chore: cascade main → dev (7b5a83c) [skip ci]' (#37) from main into dev
chore: cascade main → dev [skip ci]
2026-05-25 03:59:26 +00:00
jmiller 69ff510bac Merge pull request 'chore: cascade main → dev (8c9e3e6) [skip ci]' (#36) from main into dev
chore: cascade main → dev [skip ci]
2026-05-24 23:23:45 +00:00
12 changed files with 792 additions and 372 deletions
+54 -161
View File
@@ -96,9 +96,9 @@ jobs:
fi
MAJOR=$(echo "$VERSION" | cut -d. -f1)
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "release_tag=v${MAJOR}" >> "$GITHUB_OUTPUT"
echo "release_tag=stable" >> "$GITHUB_OUTPUT"
echo "skip=false" >> "$GITHUB_OUTPUT"
echo "branch=version/${MAJOR}" >> "$GITHUB_OUTPUT"
echo "branch=main" >> "$GITHUB_OUTPUT"
- name: "Step 1b: Bump version"
id: bump
@@ -263,6 +263,7 @@ jobs:
run: |
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/version_check.php --path . --fix 2>/dev/null || true
- name: "Step 5: Write update stream"
if: >-
@@ -270,6 +271,15 @@ jobs:
steps.platform.outputs.platform == 'joomla'
run: |
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 \
--path . --version "${VERSION}" --stability stable \
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
@@ -297,9 +307,7 @@ jobs:
# -- STEP 6: Create tag ---------------------------------------------------
- name: "Step 6: Create git tag"
if: >-
steps.version.outputs.skip != 'true' &&
steps.check.outputs.tag_exists != 'true' &&
steps.version.outputs.is_minor == 'true'
steps.version.outputs.skip != 'true'
run: |
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
# Only create the major release tag if it doesn't exist yet
@@ -453,110 +461,35 @@ jobs:
SHA256_ZIP=$(sha256sum "/tmp/${ZIP_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 }}" \
"${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 "
import sys,json
assets = json.load(sys.stdin)
for a in assets:
if a['name'] == '${ASSET_NAME}':
if a['name'] == '${ASSET}':
print(a['id']); break
" 2>/dev/null || true)
if [ -n "$ASSET_ID" ]; then
curl -sf -X DELETE -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
"${API_BASE}/releases/${RELEASE_ID}/assets/${ASSET_ID}" 2>/dev/null || true
fi
[ -n "$ASSET_ID" ] && curl -sf -X DELETE -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
"${API_BASE}/releases/${RELEASE_ID}/assets/${ASSET_ID}" 2>/dev/null || true
# Upload
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
# -- Upload both to release tag ----------------------------------
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
# updates.xml already handled by Step 5 (updates_xml_build.php with preserve logic)
echo "### Packages" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
@@ -567,73 +500,33 @@ jobs:
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
# -- STEP 8b: Update release description with changelog + SHA ----------------
- name: "Step 8b: Update release body with changelog and SHA"
# -- STEP 8b: Update release description with changelog ----------------------
- name: "Step 8b: Update release body"
if: steps.version.outputs.skip != 'true'
run: |
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
EXT_ELEMENT="${{ steps.updates.outputs.ext_element }}"
EXT_TYPE="${{ steps.updates.outputs.ext_type }}"
EXT_FOLDER="${{ steps.updates.outputs.ext_folder }}"
MOKO_CLI="/tmp/moko-platform-api/cli"
# Build TYPE_PREFIX to match Step 8's ZIP naming
EXT_ELEMENT=$(echo "$EXT_ELEMENT" | sed -E 's/^(pkg_|com_|mod_|plg_[a-z]+_|tpl_|lib_)//')
TYPE_PREFIX=""
case "${EXT_TYPE}" in
plugin) TYPE_PREFIX="plg_${EXT_FOLDER}_" ;;
module) TYPE_PREFIX="mod_" ;;
component) TYPE_PREFIX="com_" ;;
template) TYPE_PREFIX="tpl_" ;;
library) TYPE_PREFIX="lib_" ;;
package) TYPE_PREFIX="pkg_" ;;
esac
ZIP_NAME="${TYPE_PREFIX}${EXT_ELEMENT}-${VERSION}.zip"
TAR_NAME="${TYPE_PREFIX}${EXT_ELEMENT}-${VERSION}.tar.gz"
# Get SHA from the built files
SHA256_ZIP=""
[ -f "/tmp/${ZIP_NAME}" ] && SHA256_ZIP=$(sha256sum "/tmp/${ZIP_NAME}" | cut -d' ' -f1)
SHA256_TAR=""
[ -f "/tmp/${TAR_NAME}" ] && SHA256_TAR=$(sha256sum "/tmp/${TAR_NAME}" | cut -d' ' -f1)
# 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
php ${MOKO_CLI}/release_body_update.php \
--path . --version "${VERSION}" --tag "${RELEASE_TAG}" \
--token "${{ secrets.GA_TOKEN }}" \
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
2>/dev/null || {
# Fallback: simple body update if CLI not available
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
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
BODY="## ${VERSION} ($(date +%Y-%m-%d))\n\nChecksum files attached as \`*.sha256\` assets."
curl -sf -X PATCH -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
-H "Content-Type: application/json" \
"${API_BASE}/releases/${RELEASE_ID}" \
-d "{\"body\":\"${BODY}\"}" > /dev/null 2>&1
fi
}
echo "Release body updated" >> $GITHUB_STEP_SUMMARY
# -- STEP 9: Mirror to GitHub (stable only) --------------------------------
- name: "Step 9: Mirror release to GitHub"
+73
View File
@@ -0,0 +1,73 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Automation
# VERSION: 01.00.00
# BRIEF: Auto-create feature branch when an issue is opened
name: "Universal: Issue Branch"
on:
issues:
types: [opened]
permissions:
contents: write
issues: write
env:
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
jobs:
create-branch:
name: Create feature branch
runs-on: ubuntu-latest
steps:
- name: Create branch and comment
run: |
TOKEN="${{ secrets.GA_TOKEN }}"
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
ISSUE_NUM="${{ github.event.issue.number }}"
ISSUE_TITLE="${{ github.event.issue.title }}"
# Build slug from title: lowercase, replace non-alnum with dash, trim
SLUG=$(echo "${ISSUE_TITLE}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//' | cut -c1-40)
BRANCH="feature/${ISSUE_NUM}-${SLUG}"
# Check dev branch exists
DEV_EXISTS=$(curl -sf -o /dev/null -w '%{http_code}' \
-H "Authorization: token ${TOKEN}" \
"${API}/branches/dev" 2>/dev/null || echo "000")
if [ "${DEV_EXISTS}" != "200" ]; then
echo "No dev branch -- skipping"
exit 0
fi
# Create branch from dev
HTTP=$(curl -sf -o /dev/null -w '%{http_code}' -X POST \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
"${API}/branches" \
-d "{\"new_branch_name\":\"${BRANCH}\",\"old_branch_name\":\"dev\"}" 2>/dev/null || echo "000")
if [ "${HTTP}" = "201" ]; then
echo "Created branch: ${BRANCH}"
# Comment on issue with branch link
REPO_URL="${GITEA_URL}/${{ github.repository }}"
BODY="Branch created: [\`${BRANCH}\`](${REPO_URL}/src/branch/${BRANCH})\n\n\`\`\`bash\ngit fetch origin\ngit checkout ${BRANCH}\n\`\`\`"
curl -sf -X POST \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
"${API}/issues/${ISSUE_NUM}/comments" \
-d "{\"body\":\"${BODY}\"}" > /dev/null 2>&1
echo "Commented on issue #${ISSUE_NUM}"
else
echo "Failed to create branch (HTTP ${HTTP}) -- may already exist"
fi
+205 -104
View File
@@ -7,8 +7,8 @@
# INGROUP: moko-platform.Release
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
# PATH: /templates/workflows/universal/pre-release.yml.template
# VERSION: 05.00.00
# BRIEF: Manual pre-release builds dev/alpha/beta/rc packages from any branch
# VERSION: 05.01.00
# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch
name: "Universal: Pre-Release"
@@ -45,30 +45,46 @@ jobs:
fetch-depth: 0
token: ${{ secrets.GA_TOKEN }}
- name: Setup PHP
- name: Setup tools
run: |
if ! command -v php &> /dev/null; then
sudo apt-get update -qq
sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl >/dev/null 2>&1
# Update moko-platform CLI tools if available; install PHP if missing
if command -v moko-platform-update &> /dev/null; then
moko-platform-update
elif [ -d "/opt/moko-platform" ]; then
cd /opt/moko-platform && git pull origin main --quiet 2>/dev/null || true
else
if ! command -v php &> /dev/null; then
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
fi
git clone --depth 1 --branch main --quiet \
"https://x-access-token:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/moko-platform.git" \
/tmp/moko-platform-api
fi
# Set MOKO_CLI to whichever path exists
if [ -d "/opt/moko-platform/cli" ]; then
echo "MOKO_CLI=/opt/moko-platform/cli" >> "$GITHUB_ENV"
else
echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV"
fi
- name: Setup moko-platform tools
env:
MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
run: |
git clone --depth 1 --branch main --quiet "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" /tmp/moko-platform-api
- name: Detect platform
id: platform
run: |
php /tmp/moko-platform-api/cli/manifest_read.php --path . --github-output
PLATFORM=$(sed -n 's/.*<platform>\([^<]*\)<\/platform>.*/\1/p' .mokogitea/manifest.xml 2>/dev/null | head -1 | tr -d '[:space:]')
[ -z "$PLATFORM" ] && PLATFORM="generic"
echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT"
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/*" -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)
echo "manifest=${MANIFEST}" >> "$GITHUB_OUTPUT"
echo "mod_file=${MOD_FILE}" >> "$GITHUB_OUTPUT"
- name: Resolve metadata
- name: Resolve metadata and bump version
id: meta
run: |
STABILITY="${{ inputs.stability }}"
MOKO_API="/tmp/moko-platform-api/cli"
case "$STABILITY" in
development) SUFFIX="-dev"; TAG="development" ;;
@@ -77,32 +93,61 @@ jobs:
release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;;
esac
# Read current version from manifest (priority) or README — no bump yet
VERSION=$(php ${MOKO_API}/version_read.php --path .)
echo "Version: ${VERSION}"
# Patch bump via CLI tool
php ${MOKO_CLI}/version_bump.php --path .
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null)
[ -z "$VERSION" ] && VERSION="00.00.01"
TODAY=$(date +%Y-%m-%d)
# Ensure platform-specific manifest matches
php ${MOKO_API}/version_set_platform.php --path . --version "${VERSION}"
# Update platform-specific manifest
PLATFORM="${{ steps.platform.outputs.platform }}"
MANIFEST="${{ steps.platform.outputs.manifest }}"
MOD_FILE="${{ steps.platform.outputs.mod_file }}"
# Git setup for later commits
php ${MOKO_CLI}/version_set_platform.php \
--path . --version "$VERSION" --branch "${{ github.ref_name }}" 2>/dev/null || true
# Verify version consistency across all files
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
# Commit version bump
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
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 add -A
git diff --cached --quiet || {
git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]"
git push origin HEAD 2>&1
}
# Detect element from Joomla/Dolibarr manifest
set +o pipefail
PLATFORM="${{ steps.platform.outputs.platform }}"
EXT_ELEMENT=$(php ${MOKO_API}/manifest_read.php --path . --field name 2>/dev/null | tr -d ' ' | tr '[:upper:]' '[:lower:]' || true)
# For Joomla, prefer <element> tag
if [ "$PLATFORM" = "joomla" ]; then
MANIFEST=$(find . -maxdepth 4 -name "*.xml" ! -path "./.git/*" -print0 2>/dev/null | xargs -0 grep -l '<extension' 2>/dev/null | head -1 || true)
if [ -n "$MANIFEST" ]; then
ELEM=$(grep -oP "<element>\K[^<]+" "$MANIFEST" 2>/dev/null | head -1 || true)
[ -n "$ELEM" ] && EXT_ELEMENT="$ELEM"
fi
fi
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
set -o pipefail
# Auto-detect element (platform-aware)
EXT_ELEMENT=""
case "$PLATFORM" in
joomla)
if [ -n "$MANIFEST" ]; then
EXT_ELEMENT=$(sed -n 's/.*<element>\([^<]*\)<\/element>.*/\1/p' "$MANIFEST" 2>/dev/null | head -1)
if [ -z "$EXT_ELEMENT" ]; then
EXT_ELEMENT=$(basename "$MANIFEST" .xml | tr '[:upper:]' '[:lower:]')
case "$EXT_ELEMENT" in
templatedetails|manifest) EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') ;;
esac
fi
else
EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
fi
;;
dolibarr)
if [ -n "$MOD_FILE" ]; then
MOD_BASENAME=$(basename "$MOD_FILE" .class.php)
EXT_ELEMENT=$(echo "$MOD_BASENAME" | sed 's/^mod//' | tr '[:upper:]' '[:lower:]')
else
EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
fi
;;
*)
EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
;;
esac
ZIP_NAME="${EXT_ELEMENT}-${VERSION}${SUFFIX}.zip"
@@ -112,42 +157,78 @@ jobs:
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT"
echo "manifest=${MANIFEST}" >> "$GITHUB_OUTPUT"
echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ==="
- name: Build package
run: |
SOURCE_DIR="src"
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
if [ ! -d "$SOURCE_DIR" ]; then
echo "::error::No src/ or htdocs/ directory"
exit 1
fi
MANIFEST="${{ steps.meta.outputs.manifest }}"
EXT_TYPE=""
if [ -n "$MANIFEST" ]; then
EXT_TYPE=$(sed -n 's/.*<extension[^>]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
fi
EXCLUDES="sftp-config* .ftpignore *.ppk *.pem *.key .env* *.local .build-trigger"
mkdir -p build/package
REPO_ROOT=$(pwd)
if [ "$EXT_TYPE" = "package" ] && [ -d "${SOURCE_DIR}/packages" ]; then
echo "=== Building Joomla PACKAGE (multi-extension) ==="
for ext_dir in "${SOURCE_DIR}"/packages/*/; do
[ ! -d "$ext_dir" ] && continue
EXT_NAME=$(basename "$ext_dir")
echo " Packaging sub-extension: ${EXT_NAME}"
cd "$ext_dir"
zip -r "${REPO_ROOT}/build/package/${EXT_NAME}.zip" . -x $EXCLUDES
cd "${REPO_ROOT}"
done
for f in "${SOURCE_DIR}"/*.xml "${SOURCE_DIR}"/*.php; do
[ -f "$f" ] && cp "$f" build/package/
done
else
echo "=== Building standard extension ==="
rsync -a \
--exclude='sftp-config*' \
--exclude='.ftpignore' \
--exclude='*.ppk' \
--exclude='*.pem' \
--exclude='*.key' \
--exclude='.env*' \
--exclude='*.local' \
--exclude='.build-trigger' \
"${SOURCE_DIR}/" build/package/
fi
- name: Create ZIP
id: zip
run: |
VERSION="${{ steps.meta.outputs.version }}"
SUFFIX="${{ steps.meta.outputs.suffix }}"
PLATFORM="${{ steps.platform.outputs.platform }}"
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
cd build/package
zip -r "../${ZIP_NAME}" .
cd ..
if [ "$PLATFORM" = "joomla" ]; then
php /tmp/moko-platform-api/cli/joomla_build.php --path . --version "${VERSION}" --suffix "${SUFFIX}" --output build --github-output
else
# Generic build: zip src/ directory
SOURCE_DIR="src"
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
[ ! -d "$SOURCE_DIR" ] && { echo "::error::No src/ or htdocs/"; exit 1; }
EXT_ELEMENT="${{ steps.meta.outputs.ext_element }}"
ZIP_NAME="${EXT_ELEMENT}-${VERSION}${SUFFIX}.zip"
mkdir -p build
cd "$SOURCE_DIR" && zip -r "../build/${ZIP_NAME}" . && cd ..
SHA256=$(sha256sum "build/${ZIP_NAME}" | cut -d' ' -f1)
echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
echo "zip_path=build/${ZIP_NAME}" >> "$GITHUB_OUTPUT"
echo "sha256=${SHA256}" >> "$GITHUB_OUTPUT"
fi
SHA256=$(sha256sum "${ZIP_NAME}" | cut -d' ' -f1)
echo "sha256=${SHA256}" >> "$GITHUB_OUTPUT"
echo "ZIP: ${ZIP_NAME} (SHA: ${SHA256:0:16}...)"
- name: Create or replace Gitea release
id: release
continue-on-error: true
run: |
TAG="${{ steps.meta.outputs.tag }}"
VERSION="${{ steps.meta.outputs.version }}"
STABILITY="${{ steps.meta.outputs.stability }}"
SHA256="${{ steps.zip.outputs.sha256 }}"
ZIP_NAME="${{ steps.zip.outputs.zip_name }}"
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
EXT_ELEMENT="${{ steps.meta.outputs.ext_element }}"
TOKEN="${{ secrets.GA_TOKEN }}"
API="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
@@ -155,7 +236,7 @@ jobs:
BODY="## ${VERSION} ($(date +%Y-%m-%d))
**Channel:** ${STABILITY}
**SHA-256:** \`${SHA256}\`"
Checksum file attached as \`${ZIP_NAME}.sha256\`."
# Delete existing release
EXISTING_ID=$(curl -sS -H "Authorization: token ${TOKEN}" \
@@ -185,76 +266,96 @@ jobs:
curl -sS -X POST -H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/octet-stream" \
"${API}/releases/${RELEASE_ID}/assets?name=${ZIP_NAME}" \
--data-binary "@${{ steps.zip.outputs.zip_path }}"
--data-binary "@build/${ZIP_NAME}"
# Upload per-file checksum
echo "${SHA256} ${ZIP_NAME}" > "build/${ZIP_NAME}.sha256"
curl -sS -X POST -H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/octet-stream" \
"${API}/releases/${RELEASE_ID}/assets?name=${ZIP_NAME}.sha256" \
--data-binary "@build/${ZIP_NAME}.sha256"
echo "Released: ${EXT_ELEMENT} ${VERSION} (${STABILITY})"
- name: "Update updates.xml"
- name: Update updates.xml
if: steps.platform.outputs.platform == 'joomla'
run: |
VERSION="${{ steps.meta.outputs.version }}"
STABILITY="${{ steps.meta.outputs.stability }}"
VERSION="${{ steps.meta.outputs.version }}"
SHA256="${{ steps.zip.outputs.sha256 }}"
php /tmp/moko-platform-api/cli/updates_xml_build.php --path . --version "$VERSION" --stability "$STABILITY" --sha "$SHA256" --gitea-url "$GITEA_URL" --org "$GITEA_ORG" --repo "$GITEA_REPO"
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
TAG="${{ steps.meta.outputs.tag }}"
if [ ! -f "updates.xml" ]; then
echo "No updates.xml -- skipping"
exit 0
fi
# Use moko-platform CLI to build/update updates.xml (preserves other channels)
MOKO_CLI="/tmp/moko-platform-api/cli"
if [ -f "${MOKO_CLI}/updates_xml_build.php" ]; then
php "${MOKO_CLI}/updates_xml_build.php" \
--path . --version "${VERSION}" --stability "${STABILITY}" \
--sha "${SHA256}" \
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}"
echo "Updated ${STABILITY} channel via CLI: version=${VERSION}"
else
echo "WARNING: updates_xml_build.php not found — skipping"
fi
# Commit and push
if ! git diff --quiet updates.xml 2>/dev/null; then
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]"
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"
fi
- name: "Sync updates.xml to all branches"
if: steps.platform.outputs.platform == 'joomla'
run: |
php /tmp/moko-platform-api/cli/updates_xml_sync.php --path . --current "${{ github.ref_name }}" --branches main,dev --version "${{ steps.meta.outputs.version }}" --token "${{ secrets.GA_TOKEN }}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" --gitea-url "${GITEA_URL}"
CURRENT_BRANCH="${{ github.ref_name }}"
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]"
for BRANCH in main dev; do
[ "$BRANCH" = "$CURRENT_BRANCH" ] && continue
echo "Syncing updates.xml -> ${BRANCH}"
git fetch origin "${BRANCH}" 2>/dev/null || continue
git checkout "origin/${BRANCH}" -- . 2>/dev/null || continue
git checkout "${CURRENT_BRANCH}" -- updates.xml
if ! git diff --quiet updates.xml 2>/dev/null; then
git add updates.xml
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"
fi
git checkout "${CURRENT_BRANCH}" 2>/dev/null
done
- name: "Delete lesser pre-release channels (cascade)"
continue-on-error: true
run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
TOKEN="${{ secrets.GA_TOKEN }}"
STABILITY="${{ steps.meta.outputs.stability }}"
# Cascade: rc → beta,alpha,dev | beta → alpha,dev | alpha → dev | dev → nothing
case "$STABILITY" in
release-candidate) TAGS_TO_DELETE="beta alpha development" ;;
beta) TAGS_TO_DELETE="alpha development" ;;
alpha) TAGS_TO_DELETE="development" ;;
*) TAGS_TO_DELETE="" ;;
esac
php ${MOKO_CLI}/release_cascade.php \
--stability "${{ steps.meta.outputs.stability }}" \
--token "${TOKEN}" \
--api-base "${API_BASE}"
[ -z "$TAGS_TO_DELETE" ] && exit 0
for TAG in $TAGS_TO_DELETE; do
RELEASE_ID=$(curl -sS -H "Authorization: token ${TOKEN}" \
"${API_BASE}/releases/tags/${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
curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \
"${API_BASE}/releases/${RELEASE_ID}" 2>/dev/null || true
curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \
"${API_BASE}/tags/${TAG}" 2>/dev/null || true
echo "Deleted: ${TAG} (id: ${RELEASE_ID})"
fi
done
- name: "Post-release version bump"
- name: Summary
if: always()
run: |
MOKO_API="/tmp/moko-platform-api/cli"
VERSION="${{ steps.meta.outputs.version }}"
# Bump patch for next dev cycle
BUMP_OUTPUT=$(php ${MOKO_API}/version_bump.php --path .)
NEXT=$(echo "$BUMP_OUTPUT" | grep -oP '\d{2}\.\d{2}\.\d{2}$' || true)
[ -z "$NEXT" ] && exit 0
# Update platform-specific manifest to next version
php ${MOKO_API}/version_set_platform.php --path . --version "${NEXT}"
git add -A
git diff --cached --quiet || {
git commit -m "chore: update development channel ${VERSION} [skip ci]"
git push origin HEAD 2>&1
}
STABILITY="${{ steps.meta.outputs.stability }}"
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
SHA256="${{ steps.zip.outputs.sha256 }}"
echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY
echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY
echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY
+194 -15
View File
@@ -73,12 +73,12 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@v4
with:
token: ${{ secrets.GA_TOKEN }}
fetch-depth: 0
- name: Setup MokoStandards tools
- name: Setup moko-platform tools
env:
MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
@@ -87,11 +87,15 @@ jobs:
if ! command -v composer &> /dev/null; then
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1
fi
git clone --depth 1 --branch main --quiet \
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/MokoStandards-API.git" \
/tmp/mokostandards-api 2>/dev/null || true
if [ -d "/tmp/mokostandards-api" ] && [ -f "/tmp/mokostandards-api/composer.json" ]; then
cd /tmp/mokostandards-api && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
if [ -d "/tmp/moko-platform" ]; then
echo "moko-platform already available — skipping clone"
else
git clone --depth 1 --branch main --quiet \
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
/tmp/moko-platform 2>/dev/null || true
fi
if [ -d "/tmp/moko-platform" ] && [ -f "/tmp/moko-platform/composer.json" ]; then
cd /tmp/moko-platform && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
fi
- name: Generate updates.xml entry
@@ -100,14 +104,14 @@ jobs:
BRANCH="${{ github.ref_name }}"
REPO="${{ github.repository }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
VERSION=$(php /tmp/mokostandards-api/cli/version_read.php --path . 2>/dev/null || echo "0.0.0")
VERSION=$(php /tmp/moko-platform/cli/version_read.php --path . 2>/dev/null || echo "0.0.0")
# Auto-bump patch on all branches (dev, alpha, beta, rc)
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]"
BUMPED=$(php /tmp/mokostandards-api/cli/version_bump.php --path . 2>/dev/null || true)
BUMPED=$(php /tmp/moko-platform/cli/version_bump.php --path . 2>/dev/null || true)
if [ -n "$BUMPED" ]; then
VERSION=$(php /tmp/mokostandards-api/cli/version_read.php --path . 2>/dev/null || echo "$VERSION")
VERSION=$(php /tmp/moko-platform/cli/version_read.php --path . 2>/dev/null || echo "$VERSION")
git add -A
git commit -m "chore(version): auto-bump patch ${VERSION} [skip ci]" \
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>" 2>/dev/null || true
@@ -442,15 +446,190 @@ jobs:
printf ',"password":"%s"}' "$DEV_PASS" >> /tmp/sftp-config.json
fi
PLATFORM=$(php /tmp/mokostandards-api/cli/platform_detect.php --path . 2>/dev/null || true)
if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/mokostandards-api/deploy/deploy-joomla.php" ]; then
php /tmp/mokostandards-api/deploy/deploy-joomla.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
elif [ -f "/tmp/mokostandards-api/deploy/deploy-sftp.php" ]; then
php /tmp/mokostandards-api/deploy/deploy-sftp.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
PLATFORM=$(php /tmp/moko-platform/cli/platform_detect.php --path . 2>/dev/null || true)
if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/moko-platform/deploy/deploy-joomla.php" ]; then
php /tmp/moko-platform/deploy/deploy-joomla.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
elif [ -f "/tmp/moko-platform/deploy/deploy-sftp.php" ]; then
php /tmp/moko-platform/deploy/deploy-sftp.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
fi
rm -f /tmp/deploy_key /tmp/sftp-config.json
echo "SFTP deploy to dev complete" >> $GITHUB_STEP_SUMMARY
- name: Validate updates.xml integrity
run: |
ERRORS=0
if [ ! -f "updates.xml" ]; then
echo "::error::updates.xml not found"
exit 1
fi
# Well-formed XML
if ! python3 -c "import xml.etree.ElementTree as ET; ET.parse('updates.xml')" 2>/dev/null; then
echo "::error::updates.xml is not valid XML"
ERRORS=$((ERRORS+1))
fi
python3 << 'PYEOF'
import xml.etree.ElementTree as ET, sys, re, os
tree = ET.parse("updates.xml")
root = tree.getroot()
updates = root.findall("update")
errors = 0
warnings = 0
seen_tags = set()
# All 5 channels MUST be present
REQUIRED_CHANNELS = {"stable", "rc", "beta", "alpha", "dev"}
VALID_TAGS = REQUIRED_CHANNELS | {"development"} # accept legacy alias
REPO = os.environ.get("GITEA_REPO", "")
ORG = os.environ.get("GITEA_ORG", "MokoConsulting")
REPO_BASE = f"https://git.mokoconsulting.tech/{ORG}/"
# Gitea release tag names per channel (Moko standard)
RELEASE_TAG_MAP = {
"stable": "stable",
"rc": "release-candidate",
"beta": "beta",
"alpha": "alpha",
"dev": "development",
"development": "development",
}
# Joomla update XML required fields per
# https://docs.joomla.org/Deploying_an_Update_Server
REQUIRED_FIELDS = ["name", "element", "type", "version", "infourl"]
for i, u in enumerate(updates):
tag_el = u.find("tags/tag")
tag = tag_el.text.strip() if tag_el is not None and tag_el.text else None
label = f"Entry {i+1} (<tag>{tag or '?'}</tag>)"
# -- Required Joomla fields --
for field in REQUIRED_FIELDS:
el = u.find(field)
if el is None or not (el.text or "").strip():
print(f"::error::{label}: missing required <{field}>")
errors += 1
# -- <downloads><downloadurl> --
dl = u.find("downloads/downloadurl")
if dl is None or not (dl.text or "").strip():
print(f"::error::{label}: missing <downloads><downloadurl>")
errors += 1
else:
dl_url = dl.text.strip()
# Must point to org repo
if REPO_BASE not in dl_url:
print(f"::error::{label}: download URL not under {REPO_BASE}: {dl_url}")
errors += 1
# Must end in .zip
if not dl_url.endswith(".zip"):
print(f"::error::{label}: download URL must end in .zip: {dl_url}")
errors += 1
# Must use correct Gitea release tag in path
if tag and tag in RELEASE_TAG_MAP:
expected_tag = RELEASE_TAG_MAP[tag]
if f"/download/{expected_tag}/" not in dl_url:
print(f"::error::{label}: download URL should contain /download/{expected_tag}/ but got: {dl_url}")
errors += 1
# -- <client> (required for Joomla to match update) --
client = u.find("client")
if client is None or not (client.text or "").strip():
print(f"::error::{label}: missing <client> (required for Joomla update matching)")
errors += 1
# -- <targetplatform> --
tp = u.find("targetplatform")
if tp is None:
print(f"::error::{label}: missing <targetplatform>")
errors += 1
else:
tp_name = tp.get("name", "")
tp_ver = tp.get("version", "")
if tp_name != "joomla":
print(f"::error::{label}: targetplatform name should be 'joomla', got '{tp_name}'")
errors += 1
if not tp_ver:
print(f"::error::{label}: targetplatform missing version regex")
errors += 1
elif "5" not in tp_ver or "6" not in tp_ver:
print(f"::warning::{label}: targetplatform version may not cover Joomla 5+6: {tp_ver}")
warnings += 1
# -- <type> must be valid Joomla type --
type_el = u.find("type")
if type_el is not None and type_el.text:
valid_types = {"component", "module", "plugin", "template", "library", "package", "file"}
if type_el.text.strip() not in valid_types:
print(f"::error::{label}: invalid type '{type_el.text}' (expected: {valid_types})")
errors += 1
# -- <version> format (XX.YY.ZZ with optional suffix) --
ver_el = u.find("version")
if ver_el is not None and ver_el.text:
if not re.match(r"^\d{2}\.\d{2}\.\d{2}(-\w+)?$", ver_el.text.strip()):
print(f"::warning::{label}: version '{ver_el.text}' does not match XX.YY.ZZ format")
warnings += 1
# -- <maintainer> and <maintainerurl> --
for field in ["maintainer", "maintainerurl"]:
el = u.find(field)
if el is None or not (el.text or "").strip():
print(f"::warning::{label}: missing <{field}>")
warnings += 1
# -- Valid stability tag --
if tag is None:
print(f"::error::{label}: missing <tags><tag>")
errors += 1
elif tag not in VALID_TAGS:
print(f"::error::{label}: invalid tag '{tag}' (expected: {VALID_TAGS})")
errors += 1
# -- Duplicate tag check --
norm_tag = "dev" if tag == "development" else tag
if norm_tag in seen_tags:
print(f"::error::{label}: duplicate channel '{tag}'")
errors += 1
if norm_tag:
seen_tags.add(norm_tag)
# -- All 5 channels must exist --
missing = REQUIRED_CHANNELS - seen_tags
if missing:
print(f"::error::Missing required update channels: {', '.join(sorted(missing))}")
errors += 1
# -- Version ordering: higher stability must not exceed dev version --
channel_versions = {}
for u in updates:
tag_el = u.find("tags/tag")
ver_el = u.find("version")
if tag_el is not None and ver_el is not None and tag_el.text and ver_el.text:
norm = "dev" if tag_el.text.strip() == "development" else tag_el.text.strip()
# Strip suffix for comparison (01.00.18-dev -> 01.00.18)
base_ver = re.sub(r"-\w+$", "", ver_el.text.strip())
channel_versions[norm] = base_ver
# Cascade check: dev >= alpha >= beta >= rc >= stable
ORDER = ["dev", "alpha", "beta", "rc", "stable"]
for j in range(1, len(ORDER)):
current = ORDER[j]
previous = ORDER[j - 1]
if current in channel_versions and previous in channel_versions:
if channel_versions[current] > channel_versions[previous]:
print(f"::error::{current} version ({channel_versions[current]}) is ahead of {previous} ({channel_versions[previous]})")
errors += 1
# -- Summary --
print(f"\nupdates.xml validation: {len(updates)} entries, {errors} error(s), {warnings} warning(s)")
if errors > 0:
sys.exit(1)
PYEOF
- name: Summary
if: always()
run: |
+39
View File
@@ -31,6 +31,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- License/subscription check
- System email template branding (DB approach)
## [02.06.00] - 2026-05-25
### Added
- Alias offline bypass: aliases with offline=No override Joomla's global offline setting, allowing access via alias domain while main site is down
- Block non-master users from viewing or editing MokoWaaS plugin settings
- Master user bypasses ALL tenant restrictions (install from URL, global config, sysinfo, installer, templates)
### Fixed
- Install API endpoint: extract ZIP to temp directory before passing to Joomla Installer (was passing ZIP path directly)
- Clean up extracted temp directory on success or failure
- Update site disabled by Joomla when protected=1 — ensureProtectedFlag() now re-enables it
- CI: auto-release fetches updates.xml from main before building (preserves all channels)
- CI: version_check.php --fix runs after version bump in both workflows
- CI: checksums attached as `[filename].sha256` files instead of in release body
- CI: auto-release cleaned up — removed duplicate asset loops, inline Python updater, dead code
- CI: Step 8b uses `release_body_update.php` CLI instead of inline Python
- CI: Step 6 tag creation no longer gated by `is_minor` (was never set)
### Changed
- CI: auto-release uses stream tag `stable` instead of version tag `vXX`
- CI: pre-release package build uses absolute paths (fixes relative path zip error)
## [02.05.00] - 2026-05-24
### Added
- Joomla `protected=1` flag on all MokoWaaS extensions (framework-level disable/uninstall prevention)
- Self-healing protected flag — restored each admin session if cleared
- Block non-master disable via plugin list toggle (`plugins.publish`)
- Package script sets `protected=1, locked=0` on every install/update
- Legacy plugin entry in updates.xml for sites upgrading from standalone plugin
### Fixed
- CI: auto-release workflow `pkg_pkg_` duplication in release names, ZIP filenames, and SHA256 paths
- CI: auto-release now strips existing type prefix and uses `<packagename>` for packages
- CI: `updates_xml_build` was cascading entries for all lower channels on stable release — now writes only current channel
- CI: `targetplatform` regex `((5.[0-9])|(6.[0-9]))` caused Gitea 500 on XML render — simplified to `(5|6)\..*`
- updates.xml stable entry now has correct `<tag>stable</tag>` and download URL
- README slimmed to overview, detailed content moved to wiki
## [02.03.10] - 2026-05-24
### Added
+1 -1
View File
@@ -9,7 +9,7 @@
DEFGROUP: Joomla.Plugin
INGROUP: MokoWaaS
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS
VERSION: 02.04.00
VERSION: 02.08.04
PATH: /README.md
BRIEF: MokoWaaS platform plugin for Joomla
-->
+1 -1
View File
@@ -7,7 +7,7 @@
<license>GPL-3.0-or-later</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.04.00</version>
<version>02.08.04</version>
<description>Minimal API-only component for MokoWaaS. Provides REST endpoints for site health, cache, updates, and backups.</description>
<namespace path="api/src">Moko\Component\MokoWaaS\Api</namespace>
<administration>
@@ -1025,6 +1025,31 @@ class MokoWaaS extends CMSPlugin
$this->app->redirect('index.php?option=com_plugins');
}
}
// Block non-master from viewing or editing MokoWaaS plugin settings
if ($option === 'com_plugins')
{
$view = $this->app->input->get('view', '');
$layout = $this->app->input->get('layout', '');
$extensionId = (int) $this->app->input->get('extension_id', 0);
if (($view === 'plugin' || $layout === 'edit') && $extensionId > 0)
{
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select('COUNT(*)')
->from($db->quoteName('#__extensions'))
->where($db->quoteName('extension_id') . ' = ' . $extensionId)
->where($db->quoteName('element') . ' = ' . $db->quote('mokowaas'))
->where($db->quoteName('type') . ' = ' . $db->quote('plugin'));
if ((int) $db->setQuery($query)->loadResult() > 0)
{
$this->app->enqueueMessage('MokoWaaS settings are restricted to the master user.', 'warning');
$this->app->redirect('index.php?option=com_plugins');
}
}
}
}
/**
@@ -1042,6 +1067,8 @@ class MokoWaaS extends CMSPlugin
try
{
$db = Factory::getDbo();
// Set protected=1, locked=0 on MokoWaaS extensions
$query = $db->getQuery(true)
->update($db->quoteName('#__extensions'))
->set($db->quoteName('protected') . ' = 1')
@@ -1051,6 +1078,18 @@ class MokoWaaS extends CMSPlugin
->where($db->quoteName('protected') . ' = 0');
$db->setQuery($query);
$db->execute();
// Ensure update site stays enabled (protected extensions get their update site disabled by Joomla)
$query = $db->getQuery(true)
->update($db->quoteName('#__update_sites') . ' AS us')
->join('INNER', $db->quoteName('#__update_sites_extensions') . ' AS use2 ON us.update_site_id = use2.update_site_id')
->join('INNER', $db->quoteName('#__extensions') . ' AS e ON use2.extension_id = e.extension_id')
->set('us.enabled = 1')
->where('us.enabled = 0')
->where('(' . $db->quoteName('e.element') . ' = ' . $db->quote('mokowaas')
. ' OR ' . $db->quoteName('e.element') . ' = ' . $db->quote('pkg_mokowaas') . ')');
$db->setQuery($query);
$db->execute();
}
catch (\Throwable $e)
{
@@ -2747,11 +2786,36 @@ class MokoWaaS extends CMSPlugin
file_put_contents($tmpFile, $zipData);
// Extract ZIP to temp directory
$extractDir = $this->app->getConfig()->get('tmp_path', JPATH_ROOT . '/tmp')
. '/mokowaas_extract_' . md5($url);
if (is_dir($extractDir))
{
$this->rmdirRecursive($extractDir);
}
mkdir($extractDir, 0755, true);
$zip = new \ZipArchive();
if ($zip->open($tmpFile) !== true)
{
@unlink($tmpFile);
$this->sendHealthResponse(500, ['error' => 'Failed to open ZIP']);
return;
}
$zip->extractTo($extractDir);
$zip->close();
@unlink($tmpFile);
// Install using Joomla's installer
$installer = \Joomla\CMS\Installer\Installer::getInstance();
$result = $installer->install($tmpFile);
$result = $installer->install($extractDir);
@unlink($tmpFile);
$this->rmdirRecursive($extractDir);
if ($result)
{
@@ -2774,6 +2838,11 @@ class MokoWaaS extends CMSPlugin
{
@unlink($tmpFile ?? '');
if (!empty($extractDir) && is_dir($extractDir))
{
$this->rmdirRecursive($extractDir);
}
$this->sendHealthResponse(500, [
'error' => 'Install exception',
'message' => $e->getMessage(),
@@ -2782,6 +2851,42 @@ class MokoWaaS extends CMSPlugin
}
}
/**
* Recursively remove a directory.
*
* @param string $dir Directory path
*
* @return void
*
* @since 02.06.00
*/
protected function rmdirRecursive(string $dir): void
{
if (!is_dir($dir))
{
return;
}
$items = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS),
\RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($items as $item)
{
if ($item->isDir())
{
rmdir($item->getPathname());
}
else
{
unlink($item->getPathname());
}
}
rmdir($dir);
}
// ------------------------------------------------------------------
// Site Alias handling
// ------------------------------------------------------------------
@@ -2944,25 +3049,33 @@ class MokoWaaS extends CMSPlugin
}
// Offline: use Joomla's native offline mode for frontend requests
if (!empty($alias->offline) && (string) $alias->offline === '1'
&& $this->app->isClient('site'))
if ($this->app->isClient('site'))
{
// Allow health API to still respond
if ($this->app->input->get('mokowaas', '') !== '')
if (!empty($alias->offline) && (string) $alias->offline === '1')
{
return;
// Allow health API to still respond
if ($this->app->input->get('mokowaas', '') !== '')
{
return;
}
// Set custom offline message if provided
$message = $alias->offline_message ?? '';
if (!empty($message))
{
$this->app->getConfig()->set('offline_message', $message);
}
// Enable Joomla's native offline mode
$this->app->getConfig()->set('offline', 1);
}
// Set custom offline message if provided
$message = $alias->offline_message ?? '';
if (!empty($message))
else
{
$this->app->getConfig()->set('offline_message', $message);
// Alias is NOT offline — override Joomla's global offline setting
// This allows access via the alias domain even when the main site is offline
$this->app->getConfig()->set('offline', 0);
}
// Enable Joomla's native offline mode — renders through the template's offline.php
$this->app->getConfig()->set('offline', 1);
}
}
@@ -3328,12 +3441,18 @@ class MokoWaaS extends CMSPlugin
*/
protected function enforceAdminRestrictions()
{
// Master user bypasses ALL restrictions
if ($this->isMasterUser())
{
return;
}
$input = $this->app->input;
$option = $input->get('option', '');
$view = $input->get('view', '');
$task = $input->get('task', '');
// Disable install-from-URL for ALL users (safety net)
// Disable install-from-URL for non-master users
if ($this->params->get('disable_install_url', 1)
&& $option === 'com_installer'
&& stripos($task, 'install') !== false
@@ -3344,12 +3463,6 @@ class MokoWaaS extends CMSPlugin
return;
}
// Remaining restrictions only apply to non-master users
if ($this->isMasterUser())
{
return;
}
$blocked = [];
if ($this->params->get('restrict_installer', 1))
@@ -30,7 +30,7 @@
<license>GNU General Public License version 3 or later; see LICENSE.md</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.04.00</version>
<version>02.08.04</version>
<description>This plugin rebrands the Joomla system interface with MokoWaaS identity. It applies language overrides and ensures consistent branding across the platform.</description>
<namespace path=".">Moko\Plugin\System\MokoWaaS</namespace>
<scriptfile>script.php</scriptfile>
@@ -7,7 +7,7 @@
<license>GPL-3.0-or-later</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.04.00</version>
<version>02.08.04</version>
<description>Joomla Web Services API routes for MokoWaaS site management — health checks, cache, updates, backups, and site info.</description>
<namespace path="src">Moko\Plugin\WebServices\MokoWaaS</namespace>
<files>
+1 -1
View File
@@ -2,7 +2,7 @@
<extension type="package" method="upgrade">
<name>MokoWaaS</name>
<packagename>mokowaas</packagename>
<version>02.04.00</version>
<version>02.08.04</version>
<creationDate>2026-05-23</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
+86 -64
View File
@@ -1,80 +1,102 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
SPDX-License-Identifier: GPL-3.0-or-later
VERSION: 02.04.00
VERSION: 02.08.02
-->
<updates>
<update>
<name>MokoWaaS</name>
<description>MokoWaaS update</description>
<element>mokowaas</element>
<element>pkg_mokowaas</element>
<type>package</type>
<version>02.04.00-dev</version>
<tags><tag>development</tag></tags>
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/development</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/development/pkg_mokowaas-02.04.00-dev.zip</downloadurl>
</downloads>
<targetplatform name="joomla" version="(5|6)\..*" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
<update>
<name>MokoWaaS</name>
<description>MokoWaaS update</description>
<element>mokowaas</element>
<type>package</type>
<version>02.04.00-alpha</version>
<tags><tag>alpha</tag></tags>
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/alpha</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/alpha/pkg_mokowaas-02.04.00-alpha.zip</downloadurl>
</downloads>
<targetplatform name="joomla" version="(5|6)\..*" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
<update>
<name>MokoWaaS</name>
<description>MokoWaaS update</description>
<element>mokowaas</element>
<type>package</type>
<version>02.04.00-beta</version>
<tags><tag>beta</tag></tags>
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/beta</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/beta/pkg_mokowaas-02.04.00-beta.zip</downloadurl>
</downloads>
<targetplatform name="joomla" version="(5|6)\..*" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
<update>
<name>MokoWaaS</name>
<description>MokoWaaS update</description>
<element>mokowaas</element>
<type>package</type>
<version>02.04.00-rc</version>
<tags><tag>rc</tag></tags>
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/rc</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/rc/pkg_mokowaas-02.04.00-rc.zip</downloadurl>
</downloads>
<targetplatform name="joomla" version="(5|6)\..*" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
<update>
<name>MokoWaaS</name>
<description>MokoWaaS update</description>
<element>mokowaas</element>
<type>package</type>
<version>02.04.00</version>
<version>02.08.02</version>
<client>site</client>
<tags><tag>stable</tag></tags>
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/stable</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/stable/pkg_mokowaas-02.04.00.zip</downloadurl>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/development/pkg_mokowaas-02.08.02-dev.zip</downloadurl>
</downloads>
<targetplatform name="joomla" version="(5|6)\..*" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
<update>
<name>MokoWaaS</name>
<description>MokoWaaS update</description>
<element>pkg_mokowaas</element>
<type>package</type>
<version>02.08.02</version>
<client>site</client>
<tags><tag>rc</tag></tags>
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/development</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/development/pkg_mokowaas-02.08.02-dev.zip</downloadurl>
</downloads>
<targetplatform name="joomla" version="(5|6)\..*" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
<update>
<name>MokoWaaS</name>
<description>MokoWaaS update</description>
<element>pkg_mokowaas</element>
<type>package</type>
<version>02.08.02</version>
<client>site</client>
<tags><tag>beta</tag></tags>
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/development</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/development/pkg_mokowaas-02.08.02-dev.zip</downloadurl>
</downloads>
<targetplatform name="joomla" version="(5|6)\..*" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
<update>
<name>MokoWaaS</name>
<description>MokoWaaS update</description>
<element>pkg_mokowaas</element>
<type>package</type>
<version>02.08.02</version>
<client>site</client>
<tags><tag>alpha</tag></tags>
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/development</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/development/pkg_mokowaas-02.08.02-dev.zip</downloadurl>
</downloads>
<targetplatform name="joomla" version="(5|6)\..*" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
<update>
<name>MokoWaaS</name>
<description>MokoWaaS update</description>
<element>pkg_mokowaas</element>
<type>package</type>
<version>02.08.02</version>
<client>site</client>
<tags><tag>dev</tag></tags>
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/development</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/development/pkg_mokowaas-02.08.02-dev.zip</downloadurl>
</downloads>
<targetplatform name="joomla" version="(5|6)\..*" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
<update>
<name>System - MokoWaaS</name>
<description>MokoWaaS update (legacy plugin)</description>
<element>mokowaas</element>
<type>plugin</type>
<folder>system</folder>
<client>site</client>
<version>02.08.02</version>
<tags><tag>stable</tag></tags>
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/development</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/development/pkg_mokowaas-02.08.02-dev.zip</downloadurl>
</downloads>
<targetplatform name="joomla" version="(5|6)\..*" />
<maintainer>Moko Consulting</maintainer>