111 Commits

Author SHA1 Message Date
jmiller 2cf42b79b7 chore: drop duplicate workflow-sync-trigger.yml left at old path
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 3s
Universal: Auto Version Bump / Version Bump (push) Successful in 25s
Universal: PR Check / Validate PR (pull_request) Failing after 8s
Universal: PR Check / Secret Scan (pull_request) Successful in 8s
Generic: Project CI / Lint & Validate (pull_request) Failing after 12s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 9s
Universal: Build & Release / Promote to RC (pull_request) Failing after 9s
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Failing after 35s
Generic: Project CI / Tests (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
PR #36 moved this workflow to custom/; the merge of main into dev
re-surfaced the old-path copy. Remove it so dev matches main's layout.

Authored-by: Moko Consulting
2026-07-04 01:24:51 -05:00
jmiller d46c431189 refactor: restructure template into samples/ + source/ layout
Replace per-type types/<type>/ scaffolds with a samples/ reference
directory (manifest XML + install script templates for all six
extension types) and a single source/ build directory that CI scans.

- Add samples/manifest/*.xml for component, template, module, plugin,
  package, and library
- Add samples/script/*.php install/update script templates
- Add source/ as the canonical build root (CI scans source/src/htdocs)
- Remove types/ per-type scaffolds
- Rewrite root README for the new structure and expanded CI suite
- Anchor Python MANIFEST ignore rule to root (/MANIFEST) so it no
  longer swallows samples/manifest/ on case-insensitive filesystems
- Expand ci-joomla.yml: SQL, language-key, PHPCS, security, updates.xml,
  asset, MVC naming, router, ACL, webservices, ZIP dry-run, JS/CSS checks

Authored-by: Moko Consulting
2026-07-04 01:23:54 -05:00
jmiller 08a3e7b089 Merge main into dev to sync before restructure
Brings dev up to date with main so the samples/source restructure
applies cleanly. Workflow conflicts resolved in favor of main's
canonical synced versions.

Authored-by: Moko Consulting

# Conflicts:
#	.mokogitea/workflows/auto-release.yml
2026-07-04 01:23:53 -05:00
jmiller d3fa17430f Merge pull request 'fix: move workflow-sync-trigger to custom/' (#36) from fix/move-workflow-sync-trigger into main
fix: move workflow-sync-trigger to custom/ [skip ci]
2026-06-30 18:06:51 +00:00
jmiller de1b38213f chore: remove old workflow-sync-trigger from top-level [skip ci] 2026-06-30 18:03:34 +00:00
jmiller 10db4fe33a chore: move workflow-sync-trigger to custom/ [skip ci] 2026-06-30 18:03:32 +00:00
jmiller dbf24e304b fix: add submodules: recursive to checkout steps
Without this, repos with git submodules (e.g. MokoSuiteBackup with
MokoSuiteClient) produce broken/empty sub-package ZIPs because the
submodule directories are empty during the build.

Fixes: "Install path does not exist" error when installing packages
that contain nested sub-packages via submodules.

Claude-Session: https://claude.ai/code/session_01MbEjBtsSjPuTWhqqrMS2wG
2026-06-28 14:15:42 -05:00
jmiller b4c1065b57 Merge pull request 'chore(workflows): sync shared workflows from Template-Generic (dev)' (#34) from fix/sync-from-generic-dev into dev
Universal: Auto Version Bump / Version Bump (push) Has been skipped
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 13s
2026-06-27 19:19:10 +00:00
jmiller 8cd6c4626f Merge pull request 'chore(workflows): sync shared workflows from Template-Generic (main)' (#33) from fix/sync-from-generic into main 2026-06-27 19:18:43 +00:00
jmiller 757887ea11 chore(workflows): sync shared workflows from Template-Generic
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 15s
Universal: PR Check / Branch Policy (pull_request) Successful in 3s
Universal: PR Check / Validate PR (pull_request) Successful in 10s
Universal: PR Check / Secret Scan (pull_request) Successful in 11s
Generic: Project CI / Lint & Validate (pull_request) Successful in 21s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Failing after 2s
Generic: Project CI / Tests (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Bring dev's shared .mokogitea/workflows up to Template-Generic main (best
version): add ci-generic (on PR), ci-issue-reporter, hardened rc-revert,
version-set, workflow-sync-trigger; update the rest. Also normalizes CRLF->LF
per .gitattributes. Platform workflow (ci-joomla) preserved.

Authored-by: Moko Consulting
2026-06-27 14:18:07 -05:00
jmiller 7efc4731f9 chore(workflows): sync shared workflows from Template-Generic
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 14s
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 8s
Universal: PR Check / Validate PR (pull_request) Failing after 8s
Generic: Repo Health / Access control (pull_request) Successful in 3s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Secret Scan (pull_request) Successful in 12s
Generic: Project CI / Lint & Validate (pull_request) Successful in 15s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Failing after 17s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 57s
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
Bring shared .mokogitea/workflows up to Template-Generic main (best version):
hardened rc-revert.yml, ci-generic on PR, latest auto-bump/auto-release/
pre-release/workflow-sync-trigger. Platform workflows (ci-joomla,
pr-metadata-check) preserved.

Authored-by: Moko Consulting
2026-06-27 14:17:24 -05:00
jmiller d883177b88 refactor: use ci-issue-reporter reusable workflow in repo-health [skip ci] 2026-06-25 17:27:14 +00:00
jmiller 286b35fabf refactor: use ci-issue-reporter reusable workflow in pr-check [skip ci] 2026-06-25 17:26:45 +00:00
jmiller da7621a7ab feat: add ci-issue-reporter reusable workflow [skip ci] 2026-06-25 17:26:01 +00:00
Moko Consulting [bot] 219668fd3c fix: remove duplicate condition line in workflow-sync-trigger [skip ci] 2026-06-25 17:05:50 +00:00
Moko Consulting [bot] dcbd33a72f fix: allow workflow_dispatch to trigger sync job [skip ci] 2026-06-25 17:05:07 +00:00
Moko Consulting [bot] 6b6cca5761 fix: correct MOKOMOKOGITEA double prefix in workflow-sync-trigger [skip ci] 2026-06-25 17:03:23 +00:00
Moko Consulting [bot] cd6d218e76 ci: add workflow_dispatch trigger to workflow-sync-trigger [skip ci] 2026-06-25 17:00:40 +00:00
Moko Consulting [bot] c8f993c3e2 fix: standardize token/URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:58:55 +00:00
Moko Consulting [bot] d813100190 fix: standardize token/URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:55:26 +00:00
Moko Consulting [bot] cd58400dff fix: standardize token/URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:55:17 +00:00
Moko Consulting [bot] 3f8a5d8ec5 fix: standardize token/URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:55:13 +00:00
Moko Consulting [bot] 997f6c4181 fix: standardize token/URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:54:49 +00:00
Moko Consulting [bot] 3f0deac20e fix: standardize token/URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:54:45 +00:00
Moko Consulting [bot] 9964310cae fix: rename GITEA_URL to MOKOGITEA_URL in workflow-sync-trigger [skip ci] 2026-06-25 16:49:01 +00:00
Moko Consulting [bot] b1862ba8c7 fix: standardize token and URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:46:07 +00:00
Moko Consulting [bot] dba9f1c6a2 fix: standardize token and URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:46:04 +00:00
Moko Consulting [bot] 2ee87395c1 fix: standardize token and URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:46:00 +00:00
Moko Consulting [bot] 9444684322 fix: standardize token and URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:45:56 +00:00
Moko Consulting [bot] 90b632912b fix: standardize token and URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:45:52 +00:00
jmiller 3d8ec08abd Merge pull request 'fix: use metadata API for source-aware bumps instead of manifest.xml' (#32) from fix/metadata-api-source-check into main 2026-06-23 22:25:56 +00:00
Jonathan Miller db2899b3b3 fix: use first-class metadata API instead of manifest.xml for source checks
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 4s
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 3s
Universal: PR Check / Validate PR (pull_request) Failing after 4s
Universal: PR Check / Secret Scan (pull_request) Successful in 5s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 9s
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Successful in 16s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Failing after 42s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Replaces manifest.xml file parsing with the MokoGitea /manifest API
endpoint which stores platform and entry_point as database fields.

Authored-by: Moko Consulting
2026-06-23 17:25:17 -05:00
jmiller b41ed2caa5 Merge pull request 'feat: source-aware version bumps and pre-releases' (#31) from feat/source-aware-bumps into main 2026-06-23 22:13:59 +00:00
Jonathan Miller 66da3fa30c feat: make version bumps and pre-releases source-aware
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 4s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Universal: PR Check / Secret Scan (pull_request) Successful in 6s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 11s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Failing after 37s
Branch Cleanup / Delete merged branch (pull_request) Failing after 1s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Successful in 32s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Reads .mokogitea/manifest.xml to detect platform and source directory.
Skips bumps and pre-releases when only workflows, docs, or config
files change — no more empty version increments.

- auto-bump: skips if no source files changed in last commit
- pre-release: skips build if push didn't touch source (dispatch always runs)
- Falls back to always-bump for generic repos without a manifest

Authored-by: Moko Consulting
2026-06-23 17:13:02 -05:00
jmiller 36015389a4 Merge pull request 'fix: increase component search depth for package repos' (#30) from fix/ci-component-depth into main 2026-06-23 21:55:30 +00:00
Jonathan Miller 825236f94b fix: increase component manifest search depth in ci-joomla
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 14s
Universal: PR Check / Branch Policy (pull_request) Failing after 1s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 4s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Universal: PR Check / Secret Scan (pull_request) Successful in 6s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 9s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Failing after 10s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Successful in 12s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
maxdepth 4 missed components nested inside package repos
(e.g. packages/*/source/packages/com_*/manifest.xml). Increased
to maxdepth 10 and excluded .claude/ worktree paths.

Authored-by: Moko Consulting
2026-06-23 16:54:10 -05:00
jmiller f243386d8d chore: remove security-audit.yml -- handled by MokoGitea 2026-06-23 18:27:37 +00:00
jmiller 48ee0ce5bb chore: remove security-audit.yml -- handled by MokoGitea
Universal: Auto Version Bump / Version Bump (push) Successful in 6s
2026-06-23 18:05:52 +00:00
jmiller 14a3216995 Merge pull request 'fix: exclude chore branches from pre-release and enforce branch protection' (#29) from fix/chore-branch-policy into main 2026-06-23 17:42:18 +00:00
Jonathan Miller f40094f757 fix: exclude chore branches from pre-release workflow
Universal: PR Check / Branch Policy (pull_request) Failing after 1s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Universal: PR Check / Secret Scan (pull_request) Successful in 7s
Universal: PR Check / Validate PR (pull_request) Failing after 8s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 3s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 14s
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Failing after 46s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 3s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Successful in 48s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Chore branches should only touch workflows and README — they should
not trigger version bumps, pre-releases, or releases.

Authored-by: Moko Consulting
2026-06-23 12:41:10 -05:00
jmiller 4340e3a811 Merge pull request 'fix: add missing PHP setup to workflow-sync-trigger' (#28) from fix/sync-missing-php into main 2026-06-23 17:31:12 +00:00
Jonathan Miller bb739b304a fix: add missing PHP setup to workflow-sync-trigger
Universal: PR Check / Branch Policy (pull_request) Failing after 3s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 3s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 12s
Universal: PR Check / Secret Scan (pull_request) Successful in 8s
Universal: PR Check / Validate PR (pull_request) Failing after 9s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 17s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Failing after 2s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Failing after 45s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Successful in 42s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
The sync workflow calls `php /tmp/mokocli/cli/workflow_sync.php` but
never installs PHP — fails with `php: command not found` on
ubuntu-latest runners.

Authored-by: Moko Consulting
2026-06-23 12:30:25 -05:00
jmiller 628b0d4e8b Merge pull request 'feat: add version-set dispatch workflow' (#27) from feat/version-set into main 2026-06-23 17:02:27 +00:00
Jonathan Miller 8e8c4901df feat: add version-set workflow for resetting extension version
Universal: PR Check / Branch Policy (pull_request) Failing after 1s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Universal: PR Check / Secret Scan (pull_request) Successful in 6s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 13s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Generic: Repo Health / Site Health (pull_request) Has been skipped
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Branch Cleanup / Delete merged branch (pull_request) Failing after 2s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 3s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Failing after 9s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Dispatch workflow to set version across manifest, README, CHANGELOG,
and FILE INFORMATION blocks. Useful for fresh repos before first
public release.

Authored-by: Moko Consulting
2026-06-23 12:01:41 -05:00
jmiller 69ac75e35b Merge pull request 'fix: workflow audit — CI sanity checks and rename fixes' (#26) from fix/workflow-audit into main 2026-06-23 16:56:35 +00:00
Jonathan Miller ba3cefa470 fix: change duplicate file references check from error to warning
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 4s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 10s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 5s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 13s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Project CI / Lint & Validate (pull_request) Successful in 27s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Failing after 43s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 3s
Universal: Build & Release / Build & Release Pipeline (pull_request) Failing after 9s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Cross-section duplicates (e.g. same folder in <files> and <media>) are
valid in Joomla manifests — should not block CI.

Authored-by: Moko Consulting
2026-06-23 11:54:19 -05:00
Jonathan Miller 92639daf5d feat: add 6 sanity checks to ci-joomla workflow
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 20s
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 9s
Universal: PR Check / Validate PR (pull_request) Failing after 8s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: Build & Release / Promote to RC (pull_request) Failing after 15s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Generic: Project CI / Lint & Validate (pull_request) Successful in 14s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 13s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Failing after 32s
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
- scriptfile reference validation (error if missing)
- media folder/file reference validation (error if missing)
- target platform constraints warning (targetplatform, php_minimum)
- changelogurl warning (Joomla 4+ update manager)
- duplicate file/folder references in manifest (error)
- empty language keys in .ini files (warning)

Authored-by: Moko Consulting
2026-06-23 11:44:19 -05:00
Jonathan Miller 56e6ce00ae feat: add update server, dlid, and childuninstall checks to ci-joomla
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 15s
Authored-by: Moko Consulting
2026-06-23 11:35:02 -05:00
Jonathan Miller d678a7d480 chore: remove composer-publish workflow (MokoCLI-only, not for Joomla packages) 2026-06-22 09:27:39 -05:00
jmiller af066d8438 feat: skip auto-release for workflow-only and doc-only PR changes 2026-06-22 00:00:29 +00:00
jmiller b3b581c9c1 chore: remove unused Makefile - builds handled by CI auto-release 2026-06-21 23:55:09 +00:00
jmiller 5fa03c971d Merge pull request 'chore: remove automation directory' (#24) from fix/remove-automation into main 2026-06-21 23:10:05 +00:00
Jonathan Miller 3fccd56aec chore: remove automation directory
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 17s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 59s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Failing after 13s
Publish to Composer / Publish Package (release) Successful in 48s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Failing after 2s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Successful in 20s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
2026-06-21 18:03:13 -05:00
jmiller 764b98da58 chore: sync auto-release.yml with semver tag support (#304) [skip ci] 2026-06-21 16:53:34 +00:00
jmiller 9c03136d6d chore: sync auto-release.yml with semver tag support (#304) [skip ci] 2026-06-21 16:40:31 +00:00
jmiller 64566e71b5 feat: add Composer publish workflow for Gitea registry + Packagist 2026-06-21 05:46:33 +00:00
jmiller e5fdf206b7 refactor: merge PR workflows, reduce CI noise 2026-06-20 23:12:20 +00:00
jmiller 749a5140ba refactor: merge PR workflows, reduce CI noise 2026-06-20 23:12:19 +00:00
jmiller 65a6788ed0 refactor: merge PR workflows, reduce CI noise 2026-06-20 23:12:19 +00:00
jmiller a2ca2cc53d Merge pull request 'fix: add missing step IDs to auto-release, rename moko-platform to mokocli' (#23) from fix/workflow-audit into main 2026-06-20 23:06:54 +00:00
Jonathan Miller c4ba1e4c06 fix: add missing step IDs to auto-release, rename moko-platform to mokocli
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 7s
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
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Universal: PR Check / Branch Policy (pull_request) Failing after 1s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 3s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 5s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Generic: Project CI / Lint & Validate (pull_request) Successful in 9s
Generic: Repo Health / Access control (pull_request) Successful in 2s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Failing after 1s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 10s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 3s
Universal: Build & Release / Build & Release Pipeline (pull_request) Failing after 9s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Failing after 34s
Generic: Project CI / Tests (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
- auto-release.yml: add id: platform and id: version steps
- ci-joomla.yml: rename moko-platform to mokocli
- workflow-sync-trigger.yml: rename mokoplatform to mokocli
- issue-branch.yml, rc-revert.yml: update metadata comments
2026-06-20 18:03:09 -05:00
jmiller 08c121a6f2 feat: add Joomla metadata validation workflow for PRs (#21) 2026-06-20 17:28:19 +00:00
jmiller b536118817 fix: rename moko-platform to mokocli + changelog promotion in workflows 2026-06-20 17:16:07 +00:00
jmiller da816d348d fix: rename moko-platform to mokocli + changelog promotion in workflows 2026-06-20 17:16:06 +00:00
jmiller 0ddb771dca fix: rename moko-platform to mokocli + changelog promotion in workflows 2026-06-20 17:16:06 +00:00
jmiller bfd34be3cd fix: rename moko-platform to mokocli + changelog promotion in workflows 2026-06-20 17:16:05 +00:00
jmiller c90ab96a2e fix: rename moko-platform to mokocli + changelog promotion in workflows 2026-06-20 17:16:04 +00:00
jmiller a1d0b031f1 fix: rename moko-platform to mokocli + changelog promotion in workflows 2026-06-20 17:16:03 +00:00
jmiller 0f52a81692 Merge pull request 'fix: remove deprecated .mokogitea/manifest.xml' (#22) from fix into main 2026-06-19 07:10:14 +00:00
Jonathan Miller b79a16336f fix: remove deprecated .mokogitea/manifest.xml — metadata managed via API
Generic: Project CI / Lint & Validate (pull_request) Successful in 16s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 9s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Generic: Repo Health / Access control (pull_request) Successful in 3s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 1m4s
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Successful in 34s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || 'development' }}) (pull_request_target) Failing after 7s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
2026-06-19 02:05:05 -05:00
jmiller 98020c8d15 fix(ci): restore full pre-release.yml content [skip ci] 2026-06-19 02:02:49 +00:00
jmiller dd4fa3b26f fix(ci): detect rebuild by branch name not version suffix [skip ci] 2026-06-19 02:01:48 +00:00
jmiller b6fdffc4be ci: patch bump on same-branch rebuilds, minor only on elevation [skip ci] 2026-06-19 01:08:46 +00:00
jmiller 4d4ff631bd fix: update repo references from old MokoStandards names to current 2026-06-18 20:00:57 +00:00
jmiller f91f419063 fix: update repo references from old MokoStandards names to current 2026-06-18 20:00:56 +00:00
jmiller 9296d11f30 fix: update repo references from old MokoStandards names to current 2026-06-18 20:00:55 +00:00
jmiller efcc525064 fix: update repo references from old MokoStandards names to current 2026-06-18 20:00:54 +00:00
jmiller c7e253049a fix: clone mokoplatform inside runner container instead of assuming host mount 2026-06-18 19:56:11 +00:00
jmiller 6d721fd089 Merge pull request 'chore: remove deploy-manual.yml - Joomla deploys via update server' (#20) from chore/remove-deploy-manual-v2 into main 2026-06-18 18:35:12 +00:00
Jonathan Miller db7ed7fa59 chore: remove deploy-manual.yml - Joomla deploys via update server
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 7s
Universal: PR Check / Validate PR (pull_request) Failing after 8s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || 'development' }}) (pull_request_target) Failing after 12s
Generic: Project CI / Lint & Validate (pull_request) Successful in 28s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 33s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Failing after 1s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 1s
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
2026-06-18 12:57:35 -05:00
jmiller ba4454d2f7 feat: add workflow-sync-trigger - auto-sync workflows to live repos on PR merge 2026-06-18 17:08:50 +00:00
jmiller 1b56994f16 fix(ci): run repo-health on PR to main only, not on push
Universal: Auto Version Bump / Version Bump (push) Successful in 6s
2026-06-18 16:47:41 +00:00
jmiller 51807ff944 fix(ci): run repo-health on PR to main only, not on push
Universal: Auto Version Bump / Version Bump (push) Successful in 5s
2026-06-18 16:45:18 +00:00
jmiller a14bca757e fix(ci): run repo-health on PR to main only, not on push 2026-06-18 16:39:43 +00:00
jmiller 0b583f2203 ci: add changelog extraction to promote-rc job [skip ci] 2026-06-18 16:37:30 +00:00
jmiller 7c763a5bf5 fix(ci): run CI on PR only, not on push
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
2026-06-18 16:13:52 +00:00
jmiller 240bea360b chore: add rc-revert.yml workflow for RC branch cleanup
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Generic: Project CI / Lint & Validate (push) Successful in 9s
Generic: Project CI / Tests (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
2026-06-18 15:30:38 +00:00
jmiller def4e2b2f6 ci: deploy full pre-release workflow from mokoplatform [skip ci] 2026-06-18 13:42:33 +00:00
jmiller 5cd184a7dc revert: re-enable auto-bump on dev push [skip ci] 2026-06-18 13:18:36 +00:00
jmiller 3382322393 ci: disable auto-bump on push to dev [skip ci] 2026-06-16 20:46:29 +00:00
Jonathan Miller c046392574 fix(ci): use fixed-string grep for namespace check
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Project CI / Lint & Validate (push) Successful in 6s
Generic: Project CI / Tests (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
2026-06-11 19:21:00 -05:00
Jonathan Miller ce4b467865 feat(ci): add 8 Joomla-specific CI checks to ci-joomla workflow
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Project CI / Lint & Validate (push) Successful in 8s
Generic: Project CI / Tests (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
- SQL schema validation (empty files, version-named updates)
- Manifest file references (verify files/folders exist on disk)
- Form XML validation (well-formed, has field/fieldset elements)
- Deprecated Joomla API check (JFactory, JText, etc — advisory)
- Template output escaping (unescaped <?= $var ?> — advisory)
- Namespace consistency (PHP namespace matches manifest)
- SPDX license header check (advisory)
- Service provider check (interfaces, return statement)
2026-06-11 19:06:50 -05:00
Jonathan Miller bd9b711a7b feat(ci): add config.xml and access.xml validation for components
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Generic: Project CI / Lint & Validate (push) Successful in 9s
Generic: Project CI / Tests (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
Checks component extensions for access.xml (with core.admin and
core.manage actions) and config.xml, validates both are well-formed.
2026-06-11 18:28:32 -05:00
Jonathan Miller 5306ca2b27 fix(ci): restore ci-joomla.yml + make release readiness advisory
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Generic: Project CI / Lint & Validate (push) Successful in 13s
Generic: Project CI / Tests (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
Previous API commits corrupted the file. This restores the full
workflow and adds continue-on-error: true to release-readiness job.
2026-06-11 17:21:15 -05:00
jmiller 3a18f60deb fix(ci): restore ci-joomla.yml — previous commit was corrupted
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 3s
Generic: Project CI / Lint & Validate (push) Successful in 15s
Generic: Project CI / Tests (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
2026-06-11 22:04:04 +00:00
jmiller 10692d697a fix(ci): make release readiness check advisory (continue-on-error)
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 3s
Generic: Project CI / Lint & Validate (push) Successful in 15s
Generic: Project CI / Tests (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
2026-06-11 22:03:32 +00:00
jmiller ae565211d9 chore: sync notify.yml from Template-Generic [skip ci] 2026-06-07 17:51:11 +00:00
jmiller 72ec6795c0 chore: sync issue-branch.yml from Template-Generic [skip ci] 2026-06-07 17:51:11 +00:00
jmiller 12e559c21b chore: sync deploy-manual.yml from Template-Generic [skip ci] 2026-06-07 17:51:10 +00:00
jmiller e36ab75aa5 chore: sync ci-generic.yml from Template-Generic [skip ci] 2026-06-07 17:51:10 +00:00
jmiller 6109c86151 ci: add platform_detect.php to pre-release workflow [skip ci] 2026-06-07 17:40:14 +00:00
jmiller 91ea05be8e ci: sync auto-release (patch detection) and pre-release (fix branch triggers) [skip ci] 2026-06-07 11:59:21 -05:00
jmiller 7a6b7fe8b1 ci: auto pre-release on push to dev/alpha/beta/rc branches [skip ci] 2026-06-07 16:49:29 +00:00
jmiller e27bf25e6a feat: auto-detect version bump level from PR source branch [skip ci] 2026-06-07 16:49:21 +00:00
jmiller 3526b6d674 fix: gitignore site/ should be /site/ to avoid matching tmpl/site/
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
2026-06-07 03:22:57 +00:00
jmiller a02da1ad84 chore: sync .mokogitea/workflows/auto-release.yml from moko-platform [skip ci] 2026-06-06 19:50:45 +00:00
jmiller 73628c509b chore: sync .mokogitea/workflows/pre-release.yml from moko-platform [skip ci] 2026-06-06 19:48:16 +00:00
Jonathan Miller 38271472eb Merge remote-tracking branch 'origin/dev'
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
# Conflicts:
#	.mokogitea/workflows/pre-release.yml
2026-06-06 11:46:01 -05:00
jmiller 220e637970 chore: sync .mokogitea/workflows/pre-release.yml from moko-platform [skip ci] 2026-06-06 12:31:39 +00:00
jmiller 8214e8c274 chore: remove update-server workflow [skip ci] 2026-06-05 00:07:26 +00:00
jmiller 5f0860090b chore: remove update-server workflow [skip ci] 2026-06-05 00:07:26 +00:00
56 changed files with 5201 additions and 3775 deletions
+2 -2
View File
@@ -113,7 +113,7 @@ releases/
build/
dist/
out/
site/
/site/
*.map
*.css.map
*.js.map
@@ -180,7 +180,7 @@ __pycache__/
*.egg
*.egg-info/
.installed.cfg
MANIFEST
/MANIFEST
develop-eggs/
downloads/
eggs/
-25
View File
@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
MokoStandards Repository Manifest
Template: Joomla Extension
See: https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki/Home
-->
<moko-platform xmlns="https://standards.mokoconsulting.tech/moko-platform/1.0" schema-version="1.0">
<identity>
<name>Template-Joomla</name>
<org>MokoConsulting</org>
<description>Template repository for Joomla extensions (plugins, modules, components, templates)</description>
<version>01.01.00</version>
<license spdx="GPL-3.0-or-later">GNU General Public License v3</license>
</identity>
<governance>
<platform>joomla</platform>
<standards-version>05.00.00</standards-version>
<standards-source>https://git.mokoconsulting.tech/MokoConsulting/moko-platform</standards-source>
</governance>
<build>
<language>PHP</language>
<package-type>joomla-extension</package-type>
<entry-point>src/</entry-point>
</build>
</moko-platform>
+66 -66
View File
@@ -1,66 +1,66 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Release
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
# PATH: /.mokogitea/workflows/auto-bump.yml
# VERSION: 09.02.00
# BRIEF: Auto patch-bump version on every push to dev (skips merge commits)
name: "Universal: Auto Version Bump"
on:
push:
branches:
- dev
- rc
- 'feature/**'
- 'patch/**'
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
permissions:
contents: write
jobs:
bump:
name: Version Bump
runs-on: release
if: >-
!contains(github.event.head_commit.message, '[skip ci]') &&
!contains(github.event.head_commit.message, '[skip bump]') &&
!startsWith(github.event.head_commit.message, 'Merge pull request')
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
token: ${{ secrets.MOKOGITEA_TOKEN }}
fetch-depth: 1
- name: Setup moko-platform tools
run: |
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
if [ -d "/opt/moko-platform/cli" ]; then
echo "MOKO_CLI=/opt/moko-platform/cli" >> "$GITHUB_ENV"
else
git clone --depth 1 --branch main --quiet \
"https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/moko-platform.git" \
/tmp/moko-platform-api
cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet
echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV"
fi
- name: Bump version
run: |
php ${MOKO_CLI}/version_auto_bump.php \
--path . --branch "${GITHUB_REF_NAME}" \
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
--repo-url "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Release
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.mokogitea/workflows/auto-bump.yml
# VERSION: 09.02.00
# BRIEF: Auto patch-bump version on every push to dev (skips merge commits)
name: "Universal: Auto Version Bump"
on:
push:
branches:
- dev
- rc
- 'feature/**'
- 'patch/**'
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
permissions:
contents: write
jobs:
bump:
name: Version Bump
runs-on: release
if: >-
!contains(github.event.head_commit.message, '[skip ci]') &&
!contains(github.event.head_commit.message, '[skip bump]') &&
!startsWith(github.event.head_commit.message, 'Merge pull request')
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
token: ${{ secrets.MOKOGITEA_TOKEN }}
fetch-depth: 1
- name: Setup mokocli tools
run: |
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
if [ -d "/opt/mokocli/cli" ]; then
echo "MOKO_CLI=/opt/mokocli/cli" >> "$GITHUB_ENV"
else
git clone --depth 1 --branch main --quiet \
"https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/mokocli.git" \
/tmp/mokocli
cd /tmp/mokocli && composer install --no-dev --no-interaction --quiet
echo "MOKO_CLI=/tmp/mokocli/cli" >> "$GITHUB_ENV"
fi
- name: Bump version
run: |
php ${MOKO_CLI}/version_auto_bump.php \
--path . --branch "${GITHUB_REF_NAME}" \
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
--repo-url "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
+469 -285
View File
@@ -1,285 +1,469 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Release
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform
# PATH: /templates/workflows/universal/auto-release.yml.template
# VERSION: 05.00.00
# BRIEF: Universal build & release detects platform from manifest.xml
#
# +========================================================================+
# | UNIVERSAL BUILD & RELEASE PIPELINE |
# +========================================================================+
# | |
# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. |
# | |
# | Platform-specific: |
# | joomla: XML manifest, updates.xml, type-prefixed packages |
# | dolibarr: mod*.class.php, update.txt, dev version reset |
# | generic: README-only, no update stream |
# | |
# +========================================================================+
name: "Universal: Build & Release"
on:
pull_request:
types: [opened, closed]
branches:
- main
workflow_dispatch:
inputs:
action:
description: 'Action to perform'
required: false
type: choice
default: release
options:
- release
- promote-rc
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
permissions:
contents: write
jobs:
# ── PR Opened → Rename branch to RC and build RC release ─────────────────────
promote-rc:
name: Promote to RC
runs-on: release
if: >-
(github.event.action == 'opened' && github.event.pull_request.merged != true) ||
(github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc')
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
token: ${{ secrets.MOKOGITEA_TOKEN }}
fetch-depth: 1
- name: Setup moko-platform tools
env:
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
run: |
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
# Always fetch latest CLI tools — never use stale cache from previous runs
rm -rf /tmp/moko-platform-api
git clone --depth 1 --branch main --quiet \
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
/tmp/moko-platform-api
cd /tmp/moko-platform-api
composer install --no-dev --no-interaction --quiet
- name: Rename branch to rc
run: |
php /tmp/moko-platform-api/cli/branch_rename.php \
--from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
--api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \
--pr "${{ github.event.pull_request.number }}"
- name: Checkout rc and configure git
run: |
git fetch origin rc
git checkout rc
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]"
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
- name: Publish RC release
run: |
php /tmp/moko-platform-api/cli/release_publish.php \
--path . --stability rc --bump minor --branch rc \
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
--skip-update-stream
- name: Summary
if: always()
run: |
echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY
echo "Branch renamed to rc, minor bump, RC release built (updates.xml managed by Gitea Pages)" >> $GITHUB_STEP_SUMMARY
# ── Merged PR → Build & Release (or promote RC to stable) ────────────────────
release:
name: Build & Release Pipeline
runs-on: release
if: >-
github.event.pull_request.merged == true ||
(github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc')
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
token: ${{ secrets.MOKOGITEA_TOKEN }}
fetch-depth: 0
- name: Configure git for bot pushes
run: |
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]"
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
- name: Check for merge conflict markers
run: |
CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true)
if [ -n "$CONFLICTS" ]; then
echo "::error::Merge conflict markers found — aborting release"
echo "## Release Blocked: Conflict Markers" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
exit 1
fi
echo "No conflict markers found"
- name: Setup moko-platform tools
env:
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}'
run: |
# Ensure PHP + Composer are available
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
# Always fetch latest CLI tools — never use stale cache from previous runs
rm -rf /tmp/moko-platform-api
git clone --depth 1 --branch main --quiet \
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
/tmp/moko-platform-api
cd /tmp/moko-platform-api
composer install --no-dev --no-interaction --quiet
- name: "Publish stable release"
run: |
php /tmp/moko-platform-api/cli/release_publish.php \
--path . --stability stable --bump minor --branch main \
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
--skip-update-stream
# -- STEP 9: Mirror to GitHub (stable only) --------------------------------
- name: "Step 9: Mirror release to GitHub"
if: >-
steps.version.outputs.skip != 'true' &&
secrets.GH_MIRROR_TOKEN != ''
continue-on-error: true
run: |
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
php /tmp/moko-platform-api/cli/release_mirror.php \
--version "$VERSION" --tag "$RELEASE_TAG" \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
--gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \
--branch main 2>&1 || true
echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY
# -- STEP 10: Sync main branch to GitHub mirror ----------------------------
- name: "Step 10: Push main to GitHub mirror"
if: >-
steps.version.outputs.skip != 'true' &&
secrets.GH_MIRROR_TOKEN != ''
continue-on-error: true
run: |
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
GH_ORG=$(echo "$GH_REPO" | cut -d/ -f1)
GH_NAME=$(echo "$GH_REPO" | cut -d/ -f2)
git remote add github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" 2>/dev/null || \
git remote set-url github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git"
git fetch origin main --depth=1
git push github origin/main:refs/heads/main --force 2>/dev/null \
&& echo "main branch pushed to GitHub mirror" \
|| echo "WARNING: GitHub mirror push failed"
- name: "Step 11: Delete rc branch and recreate dev from main"
if: steps.version.outputs.skip != 'true'
continue-on-error: true
run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
# Delete rc branch (ephemeral — created by promote-rc)
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \
"${API_BASE}/branches/rc" 2>/dev/null \
&& echo "Deleted rc branch" || echo "rc branch not found"
# Delete dev branch
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \
"${API_BASE}/branches/dev" 2>/dev/null && echo "Deleted dev branch"
# Recreate dev from main (now includes version bump + changelog promotion)
curl -sf -X POST -H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
"${API_BASE}/branches" \
-d '{"new_branch_name":"dev","old_branch_name":"main"}' 2>/dev/null && echo "Recreated dev from main"
echo "Pre-release branches cleaned, dev reset from main" >> $GITHUB_STEP_SUMMARY
- name: "Step 12: Create version branch from main"
if: steps.version.outputs.skip != 'true'
continue-on-error: true
run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
BRANCH_NAME="version/${VERSION}"
MAIN_SHA=$(git rev-parse HEAD)
# Delete old version branch if it exists (same version re-release)
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" "${API_BASE}/branches/${BRANCH_NAME}" 2>/dev/null && echo "Deleted old ${BRANCH_NAME}"
# Create version/XX.YY.ZZ from main
curl -sf -X POST -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" "${API_BASE}/branches" -d "{\"new_branch_name\":\"${BRANCH_NAME}\",\"old_branch_name\":\"main\"}" 2>/dev/null && echo "Created ${BRANCH_NAME} from main (${MAIN_SHA})" || echo "WARNING: ${BRANCH_NAME} creation failed"
echo "Version branch created: ${BRANCH_NAME} (${MAIN_SHA})" >> $GITHUB_STEP_SUMMARY
# -- Dolibarr post-release: Reset dev version -----------------------------
- name: "Post-release: Reset dev version"
if: steps.version.outputs.skip != 'true'
continue-on-error: true
run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
php /tmp/moko-platform-api/cli/version_reset_dev.php \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \
--branch dev --path . 2>&1 || true
# -- Summary --------------------------------------------------------------
- name: Pipeline Summary
if: always()
run: |
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
PLATFORM="${{ steps.platform.outputs.platform }}"
if [ "${{ steps.version.outputs.skip }}" = "true" ]; then
echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY
echo "No VERSION in README.md" >> $GITHUB_STEP_SUMMARY
elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then
echo "## Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY
else
echo "" >> $GITHUB_STEP_SUMMARY
echo "## Build & Release Complete (${PLATFORM})" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Step | Result |" >> $GITHUB_STEP_SUMMARY
echo "|------|--------|" >> $GITHUB_STEP_SUMMARY
echo "| Platform | \`${PLATFORM}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Release | [View](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY
fi
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Release
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/mokocli
# PATH: /templates/workflows/universal/auto-release.yml.template
# VERSION: 05.01.00
# BRIEF: Universal build & release detects platform from manifest.xml
#
# +=======================================================================+
# | UNIVERSAL BUILD & RELEASE PIPELINE |
# +=======================================================================+
# | |
# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. |
# | |
# | Platform-specific: |
# | joomla: XML manifest, type-prefixed packages |
# | dolibarr: mod*.class.php, update.txt, dev version reset |
# | generic: README-only, no update stream |
# | |
# +=======================================================================+
name: "Universal: Build & Release"
on:
pull_request:
types: [opened, synchronize, closed]
branches:
- main
paths-ignore:
- '.mokogitea/workflows/**'
- '*.md'
- 'wiki/**'
- '.editorconfig'
- '.gitignore'
- '.gitattributes'
- '.gitmessage'
- 'LICENSE'
workflow_dispatch:
inputs:
action:
description: 'Action to perform'
required: false
type: choice
default: release
options:
- release
- promote-rc
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
permissions:
contents: write
jobs:
# ── PR Opened → Rename branch to RC and build RC release ─────────────────────────
promote-rc:
name: Promote to RC
runs-on: release
if: >-
(github.event.action == 'opened' && github.event.pull_request.merged != true) ||
(github.event.action == 'synchronize' && github.event.pull_request.merged != true) ||
(github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc')
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
token: ${{ secrets.MOKOGITEA_TOKEN }}
fetch-depth: 1
submodules: recursive
- name: Setup mokocli tools
env:
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
run: |
if [ -f /opt/mokocli/cli/version_bump.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then
echo Using pre-installed /opt/mokocli
echo MOKO_CLI=/opt/mokocli/cli >> $GITHUB_ENV
else
echo Falling back to fresh clone
if ! command -v composer > /dev/null 2>&1; then
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1
fi
rm -rf /tmp/mokocli
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/mokocli.git
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/mokocli
cd /tmp/mokocli
composer install --no-dev --no-interaction --quiet
echo MOKO_CLI=/tmp/mokocli/cli >> $GITHUB_ENV
fi
- name: Rename branch to rc
run: |
php ${MOKO_CLI}/branch_rename.php \
--from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
--api-base "${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \
--pr "${{ github.event.pull_request.number }}"
- name: Checkout rc and configure git
run: |
git fetch origin rc
git checkout rc
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]"
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
- name: Publish RC release
run: |
php ${MOKO_CLI}/release_publish.php \
--path . --stability rc --bump minor --branch rc \
--token "${{ secrets.MOKOGITEA_TOKEN }}"
- name: Update RC release notes from CHANGELOG.md
run: |
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
# Extract [Unreleased] section from changelog
NOTES=""
if [ -f "CHANGELOG.md" ]; then
NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md)
fi
[ -z "$NOTES" ] && NOTES="Release candidate"
# Find the RC release and update its body
RELEASE_ID=$(curl -sf -H "Authorization: token ${TOKEN}" \
"${API_BASE}/releases/tags/release-candidate" \
| python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
if [ -n "$RELEASE_ID" ]; then
python3 -c "
import json, urllib.request
body = open('/dev/stdin').read()
payload = json.dumps({'body': body}).encode()
req = urllib.request.Request(
'${API_BASE}/releases/${RELEASE_ID}',
data=payload, method='PATCH',
headers={
'Authorization': 'token ${TOKEN}',
'Content-Type': 'application/json'
})
urllib.request.urlopen(req)
" <<< "$NOTES"
echo "RC release notes updated from CHANGELOG.md"
fi
- name: Summary
if: always()
run: |
echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY
echo "Branch renamed to rc, minor bump, RC release built" >> $GITHUB_STEP_SUMMARY
# ── Merged PR → Build & Release (or promote RC to stable) ─────────────────────────
release:
name: Build & Release Pipeline
runs-on: release
if: >-
github.event.pull_request.merged == true ||
(github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc')
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
token: ${{ secrets.MOKOGITEA_TOKEN }}
fetch-depth: 0
submodules: recursive
- name: Configure git for bot pushes
run: |
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]"
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
- name: Check for merge conflict markers
run: |
CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true)
if [ -n "$CONFLICTS" ]; then
echo "::error::Merge conflict markers found — aborting release"
echo "## Release Blocked: Conflict Markers" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
exit 1
fi
echo "No conflict markers found"
- name: Setup mokocli tools
env:
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}'
run: |
if [ -f /opt/mokocli/cli/version_bump.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then
echo Using pre-installed /opt/mokocli
echo MOKO_CLI=/opt/mokocli/cli >> $GITHUB_ENV
else
echo Falling back to fresh clone
if ! command -v composer > /dev/null 2>&1; then
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1
fi
rm -rf /tmp/mokocli
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/mokocli.git
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/mokocli
cd /tmp/mokocli
composer install --no-dev --no-interaction --quiet
echo MOKO_CLI=/tmp/mokocli/cli >> $GITHUB_ENV
fi
- name: "Detect platform"
id: platform
run: |
php ${MOKO_CLI}/platform_detect.php --path . --github-output 2>/dev/null || true
php ${MOKO_CLI}/manifest_read.php --path . --github-output 2>/dev/null || true
- name: "Determine version bump level"
id: bump
run: |
# Fix/patch branches: version was already bumped by pre-release, just strip suffix
# Feature/dev branches: bump minor for the new stable release
HEAD_REF="${{ github.event.pull_request.head.ref || 'dev' }}"
case "$HEAD_REF" in
fix/*|patch/*|hotfix/*|bugfix/*) BUMP="none" ;;
*) BUMP="minor" ;;
esac
echo "level=${BUMP}" >> "$GITHUB_OUTPUT"
echo "Bump level: ${BUMP} (from branch: ${HEAD_REF})"
- name: "Publish stable release"
run: |
BUMP_FLAG=""
if [ "${{ steps.bump.outputs.level }}" != "none" ]; then
BUMP_FLAG="--bump ${{ steps.bump.outputs.level }}"
fi
php ${MOKO_CLI}/release_publish.php \
--path . --stability stable ${BUMP_FLAG} --branch main \
--token "${{ secrets.MOKOGITEA_TOKEN }}"
- name: "Read published version"
id: version
run: |
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "")
VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//')
[ -z "$VERSION" ] && VERSION="00.00.00" && echo "skip=true" >> "$GITHUB_OUTPUT"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
PLATFORM="${{ steps.platform.outputs.platform }}"
if [[ "$PLATFORM" == joomla* ]]; then
echo "tag=stable" >> "$GITHUB_OUTPUT"
echo "release_tag=stable" >> "$GITHUB_OUTPUT"
else
echo "tag=v${VERSION}" >> "$GITHUB_OUTPUT"
echo "release_tag=v${VERSION}" >> "$GITHUB_OUTPUT"
fi
echo "branch=main" >> "$GITHUB_OUTPUT"
echo "Published version: ${VERSION}"
- name: "Create semver tag for non-Joomla repos"
id: semver
if: |
steps.version.outputs.skip != 'true' &&
!startsWith(steps.platform.outputs.platform, 'joomla')
run: |
VERSION="${{ steps.version.outputs.version }}"
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
SEMVER_TAG="v${VERSION}"
echo "Creating semver tag: ${SEMVER_TAG}"
# Create the git tag via API
HTTP_CODE=$(curl -sf -o /dev/null -w "%{http_code}" \
-X POST -H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
"${API_BASE}/tags" \
-d "{\"tag_name\":\"${SEMVER_TAG}\",\"target\":\"main\",\"message\":\"Release ${VERSION}\"}" 2>/dev/null || echo "000")
if [ "$HTTP_CODE" = "201" ] || [ "$HTTP_CODE" = "200" ]; then
echo "Created semver tag: ${SEMVER_TAG}"
elif [ "$HTTP_CODE" = "409" ]; then
echo "Semver tag ${SEMVER_TAG} already exists (skipped)"
else
echo "::warning::Failed to create semver tag ${SEMVER_TAG} (HTTP ${HTTP_CODE})"
fi
echo "semver_tag=${SEMVER_TAG}" >> "$GITHUB_OUTPUT"
- name: Update release notes and promote changelog
run: |
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
# Get the stable release info (version and ID)
RELEASE_JSON=$(curl -sf -H "Authorization: token ${TOKEN}" \
"${API_BASE}/releases/tags/stable" 2>/dev/null || echo '{}')
RELEASE_ID=$(python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" <<< "$RELEASE_JSON" 2>/dev/null || true)
# Extract version from release name (e.g. "06.17.00" or "v06.17.00")
VERSION=$(python3 -c "
import json, sys, re
r = json.load(sys.stdin)
name = r.get('name', '')
m = re.search(r'(\d+\.\d+\.\d+)', name)
print(m.group(1) if m else '')
" <<< "$RELEASE_JSON" 2>/dev/null || true)
# Extract [Unreleased] section from changelog
NOTES=""
if [ -f "CHANGELOG.md" ]; then
NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md)
fi
[ -z "$NOTES" ] && NOTES="Stable release"
# Update release body via API
if [ -n "$RELEASE_ID" ]; then
python3 -c "
import json, urllib.request
body = open('/dev/stdin').read()
payload = json.dumps({'body': body}).encode()
req = urllib.request.Request(
'${API_BASE}/releases/${RELEASE_ID}',
data=payload, method='PATCH',
headers={
'Authorization': 'token ${TOKEN}',
'Content-Type': 'application/json'
})
urllib.request.urlopen(req)
" <<< "$NOTES"
echo "Release notes updated from CHANGELOG.md"
fi
# Promote [Unreleased] → [version] in CHANGELOG.md and reset
if [ -n "$VERSION" ] && [ -f "CHANGELOG.md" ]; then
DATE=$(date +%Y-%m-%d)
python3 -c "
import sys
version, date = sys.argv[1], sys.argv[2]
content = open('CHANGELOG.md').read()
old = '## [Unreleased]'
new = f'## [Unreleased]\n\n## [{version}] --- {date}'
content = content.replace(old, new, 1)
open('CHANGELOG.md', 'w').write(content)
" "$VERSION" "$DATE"
git add CHANGELOG.md
git commit -m "chore: promote changelog [Unreleased] → [${VERSION}]" || true
git push origin main || true
echo "Changelog promoted: [Unreleased] → [${VERSION}]"
fi
# -- STEP 9: Mirror to GitHub (stable only) --------------------------------
- name: "Step 9: Mirror release to GitHub"
if: >-
steps.version.outputs.skip != 'true' &&
secrets.GH_MIRROR_TOKEN != ''
continue-on-error: true
run: |
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
php ${MOKO_CLI}/release_mirror.php \
--version "$VERSION" --tag "$RELEASE_TAG" \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
--gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \
--branch main 2>&1 || true
echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY
# -- STEP 10: Sync main branch to GitHub mirror ----------------------------
- name: "Step 10: Push main to GitHub mirror"
if: >-
steps.version.outputs.skip != 'true' &&
secrets.GH_MIRROR_TOKEN != ''
continue-on-error: true
run: |
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
GH_ORG=$(echo "$GH_REPO" | cut -d/ -f1)
GH_NAME=$(echo "$GH_REPO" | cut -d/ -f2)
git remote add github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" 2>/dev/null || \
git remote set-url github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git"
git fetch origin main --depth=1
git push github origin/main:refs/heads/main --force 2>/dev/null \
&& echo "main branch pushed to GitHub mirror" \
|| echo "WARNING: GitHub mirror push failed"
- name: "Step 11: Delete rc branch and recreate dev from main"
if: steps.version.outputs.skip != 'true'
continue-on-error: true
run: |
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
# Delete rc branch (ephemeral — created by promote-rc)
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \
"${API_BASE}/branches/rc" 2>/dev/null \
&& echo "Deleted rc branch" || echo "rc branch not found"
# Delete dev branch
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \
"${API_BASE}/branches/dev" 2>/dev/null && echo "Deleted dev branch"
# Recreate dev from main (now includes version bump + changelog promotion)
curl -sf -X POST -H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
"${API_BASE}/branches" \
-d '{"new_branch_name":"dev","old_branch_name":"main"}' 2>/dev/null && echo "Recreated dev from main"
echo "Pre-release branches cleaned, dev reset from main" >> $GITHUB_STEP_SUMMARY
- name: "Step 12: Create version branch from main"
if: steps.version.outputs.skip != 'true'
continue-on-error: true
run: |
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
BRANCH_NAME="version/${VERSION}"
MAIN_SHA=$(git rev-parse HEAD)
# Delete old version branch if it exists (same version re-release)
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" "${API_BASE}/branches/${BRANCH_NAME}" 2>/dev/null && echo "Deleted old ${BRANCH_NAME}"
# Create version/XX.YY.ZZ from main
curl -sf -X POST -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" "${API_BASE}/branches" -d "{\"new_branch_name\":\"${BRANCH_NAME}\",\"old_branch_name\":\"main\"}" 2>/dev/null && echo "Created ${BRANCH_NAME} from main (${MAIN_SHA})" || echo "WARNING: ${BRANCH_NAME} creation failed"
echo "Version branch created: ${BRANCH_NAME} (${MAIN_SHA})" >> $GITHUB_STEP_SUMMARY
# -- Dolibarr post-release: Reset dev version -----------------------------
- name: "Post-release: Reset dev version"
if: steps.version.outputs.skip != 'true'
continue-on-error: true
run: |
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
php ${MOKO_CLI}/version_reset_dev.php \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \
--branch dev --path . 2>&1 || true
# -- Summary --------------------------------------------------------------
- name: Pipeline Summary
if: always()
run: |
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
PLATFORM="${{ steps.platform.outputs.platform }}"
if [ "${{ steps.version.outputs.skip }}" = "true" ]; then
echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY
echo "No VERSION in README.md" >> $GITHUB_STEP_SUMMARY
elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then
echo "## Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY
else
echo "" >> $GITHUB_STEP_SUMMARY
echo "## Build & Release Complete (${PLATFORM})" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Step | Result |" >> $GITHUB_STEP_SUMMARY
echo "|------|--------|" >> $GITHUB_STEP_SUMMARY
echo "| Platform | \`${PLATFORM}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Release | [View](${MOKOGITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY
fi
+1 -1
View File
@@ -5,7 +5,7 @@
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.Universal
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.mokogitea/workflows/branch-cleanup.yml
# VERSION: 01.00.00
# BRIEF: Delete feature branches after PR merge
+197
View File
@@ -0,0 +1,197 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.CI
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
# PATH: /.gitea/workflows/ci-generic.yml
# VERSION: 01.00.00
# BRIEF: CI pipeline — lint, validate, and test for generic projects (PHP + Node.js)
name: "Generic: Project CI"
on:
pull_request:
branches:
- main
- dev
- dev/**
- rc/**
workflow_dispatch:
permissions:
contents: read
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
# ── Lint & Validate ───────────────────────────────────────────────────
lint:
name: Lint & Validate
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Detect toolchain
id: detect
run: |
HAS_PHP=false
HAS_NODE=false
[ -f "composer.json" ] && HAS_PHP=true
[ -f "package.json" ] && HAS_NODE=true
echo "has_php=$HAS_PHP" >> "$GITHUB_OUTPUT"
echo "has_node=$HAS_NODE" >> "$GITHUB_OUTPUT"
echo "Toolchain: PHP=$HAS_PHP Node=$HAS_NODE"
- name: Setup PHP
if: steps.detect.outputs.has_php == 'true'
run: |
if ! command -v php &> /dev/null; then
sudo apt-get update -qq
sudo apt-get install -y -qq php-cli php-mbstring php-xml >/dev/null 2>&1
fi
php -v
- name: Setup Node.js
if: steps.detect.outputs.has_node == 'true'
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install PHP dependencies
if: steps.detect.outputs.has_php == 'true'
run: |
if [ -f "composer.json" ]; then
composer install --no-interaction --prefer-dist --quiet 2>/dev/null || true
fi
- name: Install Node.js dependencies
if: steps.detect.outputs.has_node == 'true'
run: |
if [ -f "package.json" ]; then
npm ci --quiet 2>/dev/null || npm install --quiet 2>/dev/null || true
fi
- name: PHP syntax check
if: steps.detect.outputs.has_php == 'true'
run: |
ERRORS=0
while IFS= read -r -d '' file; do
if ! php -l "$file" 2>&1 | grep -q "No syntax errors"; then
echo "::error file=${file}::PHP syntax error"
ERRORS=$((ERRORS + 1))
fi
done < <(find . -name "*.php" -not -path "./.git/*" -not -path "./vendor/*" -not -path "./node_modules/*" -print0)
echo "## PHP Lint" >> $GITHUB_STEP_SUMMARY
if [ "$ERRORS" -eq 0 ]; then
echo "All PHP files passed syntax check." >> $GITHUB_STEP_SUMMARY
else
echo "${ERRORS} file(s) with syntax errors." >> $GITHUB_STEP_SUMMARY
exit 1
fi
- name: TypeScript/JavaScript lint
if: steps.detect.outputs.has_node == 'true'
run: |
if [ -f "node_modules/.bin/eslint" ]; then
npx eslint src/ --quiet 2>&1 || { echo "::error::ESLint errors found"; exit 1; }
echo "## ESLint" >> $GITHUB_STEP_SUMMARY
echo "All files passed ESLint." >> $GITHUB_STEP_SUMMARY
elif [ -f ".eslintrc.json" ] || [ -f ".eslintrc.js" ] || [ -f "eslint.config.js" ]; then
echo "::warning::ESLint config found but eslint not installed"
else
echo "No ESLint configured — skipping"
fi
- name: TypeScript compile check
if: steps.detect.outputs.has_node == 'true'
run: |
if [ -f "tsconfig.json" ] && [ -f "node_modules/.bin/tsc" ]; then
npx tsc --noEmit 2>&1 || { echo "::error::TypeScript compilation errors"; exit 1; }
echo "## TypeScript" >> $GITHUB_STEP_SUMMARY
echo "TypeScript compilation passed." >> $GITHUB_STEP_SUMMARY
fi
- name: PHPStan static analysis
if: steps.detect.outputs.has_php == 'true'
run: |
if [ -f "phpstan.neon" ] && [ -f "vendor/bin/phpstan" ]; then
vendor/bin/phpstan analyse --no-progress 2>&1 || { echo "::warning::PHPStan found issues"; }
fi
# ── Tests ─────────────────────────────────────────────────────────────
test:
name: Tests
runs-on: ubuntu-latest
needs: lint
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Detect toolchain
id: detect
run: |
HAS_PHP=false
HAS_NODE=false
[ -f "composer.json" ] && HAS_PHP=true
[ -f "package.json" ] && HAS_NODE=true
echo "has_php=$HAS_PHP" >> "$GITHUB_OUTPUT"
echo "has_node=$HAS_NODE" >> "$GITHUB_OUTPUT"
- name: Setup PHP
if: steps.detect.outputs.has_php == 'true'
run: |
if ! command -v php &> /dev/null; then
sudo apt-get update -qq
sudo apt-get install -y -qq php-cli php-mbstring php-xml >/dev/null 2>&1
fi
- name: Setup Node.js
if: steps.detect.outputs.has_node == 'true'
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: |
[ -f "composer.json" ] && composer install --no-interaction --prefer-dist --quiet 2>/dev/null || true
[ -f "package.json" ] && { npm ci --quiet 2>/dev/null || npm install --quiet 2>/dev/null || true; }
- name: Run PHP tests
if: steps.detect.outputs.has_php == 'true'
run: |
if [ -f "vendor/bin/phpunit" ]; then
vendor/bin/phpunit --testdox 2>&1
echo "## PHPUnit" >> $GITHUB_STEP_SUMMARY
echo "Tests passed." >> $GITHUB_STEP_SUMMARY
elif [ -f "phpunit.xml" ] || [ -f "phpunit.xml.dist" ]; then
echo "::warning::PHPUnit config found but phpunit not installed"
else
echo "No PHPUnit configured — skipping"
fi
- name: Run Node.js tests
if: steps.detect.outputs.has_node == 'true'
run: |
if jq -e '.scripts.test' package.json > /dev/null 2>&1; then
npm test 2>&1
echo "## Node.js Tests" >> $GITHUB_STEP_SUMMARY
echo "Tests passed." >> $GITHUB_STEP_SUMMARY
else
echo "No test script in package.json — skipping"
fi
- name: Build check
run: |
if [ -f "Makefile" ]; then
make build 2>&1 || echo "::warning::Build failed or not configured"
elif [ -f "package.json" ] && jq -e '.scripts.build' package.json > /dev/null 2>&1; then
npm run build 2>&1 || echo "::warning::Build failed"
fi
@@ -0,0 +1,68 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Universal
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.mokogitea/workflows/ci-issue-reporter.yml
# VERSION: 01.00.00
# BRIEF: Reusable workflow — creates/updates a Gitea issue when a CI gate fails.
# Clones MokoCLI and runs cli/ci_issue_reporter.sh.
name: "Universal: CI Issue Reporter"
on:
workflow_call:
inputs:
gate:
description: "CI gate name (e.g. PR Validation, Repository Health)"
required: true
type: string
details:
description: "Human-readable failure description"
required: true
type: string
severity:
description: "error or warning"
required: false
type: string
default: "error"
workflow:
description: "Workflow name for the issue title"
required: false
type: string
default: ""
secrets:
MOKOGITEA_TOKEN:
required: true
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
report:
name: "Report: ${{ inputs.gate }}"
runs-on: ubuntu-latest
steps:
- name: Clone MokoCLI
env:
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
run: |
MOKOGITEA_URL="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}"
git clone --depth 1 --filter=blob:none --sparse "${MOKOGITEA_URL}/MokoConsulting/MokoCLI.git" /tmp/mokocli
cd /tmp/mokocli && git sparse-checkout set cli/ci_issue_reporter.sh
- name: Report CI failure
env:
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
run: |
chmod +x /tmp/mokocli/cli/ci_issue_reporter.sh
/tmp/mokocli/cli/ci_issue_reporter.sh \
--gate "${{ inputs.gate }}" \
--details "${{ inputs.details }}" \
--severity "${{ inputs.severity }}" \
--workflow "${{ inputs.workflow }}"
File diff suppressed because it is too large Load Diff
+10 -10
View File
@@ -21,7 +21,7 @@ permissions:
contents: write
env:
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
jobs:
cleanup:
@@ -33,17 +33,17 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GA_TOKEN }}
token: ${{ secrets.MOKOGITEA_TOKEN }}
- name: Delete merged branches
env:
GA_TOKEN: ${{ secrets.GA_TOKEN }}
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
run: |
echo "=== Merged Branch Cleanup ==="
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
API="${MOKOGITEA_URL}/api/v1/repos/${{ github.repository }}"
# List branches via API
BRANCHES=$(curl -sS -H "Authorization: token ${GA_TOKEN}" \
BRANCHES=$(curl -sS -H "Authorization: token ${MOKOGITEA_TOKEN}" \
"${API}/branches?limit=50" | jq -r '.[].name')
DELETED=0
@@ -56,7 +56,7 @@ jobs:
# Check if branch is merged into main
if git merge-base --is-ancestor "origin/${BRANCH}" origin/main 2>/dev/null; then
echo " Deleting merged branch: ${BRANCH}"
curl -sS -X DELETE -H "Authorization: token ${GA_TOKEN}" \
curl -sS -X DELETE -H "Authorization: token ${MOKOGITEA_TOKEN}" \
"${API}/branches/${BRANCH}" 2>/dev/null || true
DELETED=$((DELETED + 1))
fi
@@ -66,20 +66,20 @@ jobs:
- name: Clean old workflow runs
env:
GA_TOKEN: ${{ secrets.GA_TOKEN }}
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
run: |
echo "=== Workflow Run Cleanup ==="
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
API="${MOKOGITEA_URL}/api/v1/repos/${{ github.repository }}"
CUTOFF=$(date -d "30 days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -v-30d +%Y-%m-%dT%H:%M:%SZ)
# Get old completed runs
RUNS=$(curl -sS -H "Authorization: token ${GA_TOKEN}" \
RUNS=$(curl -sS -H "Authorization: token ${MOKOGITEA_TOKEN}" \
"${API}/actions/runs?status=completed&limit=50" | \
jq -r ".workflow_runs[] | select(.created_at < \"${CUTOFF}\") | .id" 2>/dev/null)
DELETED=0
for RUN_ID in $RUNS; do
curl -sS -X DELETE -H "Authorization: token ${GA_TOKEN}" \
curl -sS -X DELETE -H "Authorization: token ${MOKOGITEA_TOKEN}" \
"${API}/actions/runs/${RUN_ID}" 2>/dev/null || true
DELETED=$((DELETED + 1))
done
@@ -0,0 +1,81 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Universal
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.mokogitea/workflows/workflow-sync-trigger.yml
# VERSION: 01.01.00
# BRIEF: Trigger workflow sync to live repos when a PR is merged to main
name: "Universal: Workflow Sync Trigger"
on:
workflow_dispatch:
pull_request:
types: [closed]
branches:
- main
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
sync:
name: Sync workflows to live repos
runs-on: ubuntu-latest
if: >-
github.event_name == 'workflow_dispatch' ||
(github.event.pull_request.merged == true &&
!contains(github.event.pull_request.title, '[skip sync]'))
steps:
- name: Determine platform from repo name
id: platform
run: |
REPO="${{ github.event.repository.name }}"
case "$REPO" in
Template-Joomla) PLATFORM="joomla" ;;
Template-Dolibarr) PLATFORM="dolibarr" ;;
Template-Go) PLATFORM="go" ;;
Template-MCP) PLATFORM="mcp" ;;
Template-Generic) PLATFORM="" ;;
*) PLATFORM="" ;;
esac
echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT"
echo "Platform: ${PLATFORM:-all}"
- name: Clone mokocli
env:
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
run: |
MOKOGITEA_URL="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}"
git clone --depth 1 "${MOKOGITEA_URL}/MokoConsulting/mokocli.git" /tmp/mokocli
- name: Install PHP
run: |
if ! command -v php &> /dev/null; then
apt-get update -qq && apt-get install -y -qq php-cli php-json php-curl > /dev/null 2>&1
fi
- name: Install dependencies
run: |
cd /tmp/mokocli
composer install --no-dev --no-interaction --quiet 2>/dev/null || true
- name: Run workflow sync
env:
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
run: |
ARGS="--token ${MOKOGITEA_TOKEN}"
ARGS="${ARGS} --org ${{ vars.GITEA_ORG || github.repository_owner }}"
ARGS="${ARGS} --phase repos"
PLATFORM="${{ steps.platform.outputs.platform }}"
if [ -n "$PLATFORM" ]; then
ARGS="${ARGS} --platform-filter ${PLATFORM}"
fi
php /tmp/mokocli/cli/workflow_sync.php ${ARGS}
-4
View File
@@ -25,10 +25,6 @@
name: "Universal: Secret Scanning"
on:
pull_request:
branches:
- main
- 'dev/**'
schedule:
- cron: '0 5 * * 1' # Weekly Monday 05:00 UTC
workflow_dispatch:
+6 -6
View File
@@ -4,8 +4,8 @@
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Automation
# VERSION: 01.01.00
# INGROUP: mokocli.Automation
# VERSION: 01.00.00
# BRIEF: Auto-create feature branch when an issue is opened
name: "Universal: Issue Branch"
@@ -19,7 +19,7 @@ permissions:
issues: write
env:
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
jobs:
create-branch:
@@ -28,8 +28,8 @@ jobs:
steps:
- name: Create branch and comment
run: |
TOKEN="${{ secrets.GA_TOKEN }}"
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
API="${MOKOGITEA_URL}/api/v1/repos/${{ github.repository }}"
ISSUE_NUM="${{ github.event.issue.number }}"
ISSUE_TITLE="${{ github.event.issue.title }}"
@@ -58,7 +58,7 @@ jobs:
echo "Created branch: ${BRANCH}"
# Comment on issue with branch link
REPO_URL="${GITEA_URL}/${{ github.repository }}"
REPO_URL="${MOKOGITEA_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 \
-1
View File
@@ -18,7 +18,6 @@ on:
- "Joomla Build & Release"
- "Joomla Extension CI"
- "Deploy"
- "Cascade Main → Dev"
types:
- completed
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,71 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Validation
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /templates/workflows/joomla/pr-metadata-check.yml.template
# VERSION: 01.00.00
# BRIEF: Validate MokoGitea metadata matches Joomla extension manifest on PRs
name: "Joomla: Metadata Validation"
on:
pull_request:
types: [opened, synchronize, reopened, converted_to_draft, ready_for_review]
permissions:
contents: read
env:
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
jobs:
validate-metadata:
name: "Validate Joomla Metadata"
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup mokocli tools
env:
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
run: |
if [ -f /opt/mokocli/cli/joomla_metadata_validate.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then
echo Using pre-installed /opt/mokocli
echo MOKO_CLI=/opt/mokocli/cli >> $GITHUB_ENV
else
echo Falling back to fresh clone
if ! command -v composer > /dev/null 2>&1; then
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1
fi
rm -rf /tmp/mokocli
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/mokocli.git
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/mokocli
cd /tmp/mokocli && composer install --no-dev --no-interaction --quiet
echo MOKO_CLI=/tmp/mokocli/cli >> $GITHUB_ENV
fi
- name: Validate metadata against Joomla manifest
env:
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
run: |
php ${MOKO_CLI}/joomla_metadata_validate.php \
--path . \
--token "${MOKOGITEA_TOKEN}" \
--org "${GITEA_ORG}" \
--repo "${GITEA_REPO}" \
--api-base "${MOKOGITEA_URL}/api/v1" \
--ci
if [ $? -ne 0 ]; then
echo "::error::Joomla metadata mismatch — update delivery will fail. Run 'php cli/joomla_metadata_validate.php' locally to see details."
exit 1
fi
+107 -93
View File
@@ -4,23 +4,26 @@
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Release
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
# INGROUP: mokocli.Release
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /templates/workflows/universal/pre-release.yml.template
# VERSION: 05.01.00
# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch
# VERSION: 05.02.00
# BRIEF: Auto pre-release on push to dev/alpha/beta/rc branches
name: "Universal: Pre-Release"
on:
pull_request:
types: [closed]
push:
branches:
- dev
pull_request_target:
types: [synchronize, opened, reopened]
branches:
- main
- 'fix/**'
- 'patch/**'
- 'hotfix/**'
- 'bugfix/**'
- 'chore/**'
- alpha
- beta
- rc
workflow_dispatch:
inputs:
stability:
@@ -43,12 +46,11 @@ env:
jobs:
build:
name: "Build Pre-Release (${{ inputs.stability || 'development' }})"
name: "Build Pre-Release (${{ inputs.stability || github.ref_name }})"
runs-on: release
if: >-
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'pull_request' && github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev') ||
(github.event_name == 'pull_request_target' && github.event.pull_request.base.ref == 'main')
github.event_name == 'push'
steps:
- name: Checkout
@@ -56,35 +58,64 @@ jobs:
with:
fetch-depth: 0
token: ${{ secrets.MOKOGITEA_TOKEN }}
ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || '' }}
ref: ${{ github.ref_name }}
submodules: recursive
- name: Setup moko-platform tools
- name: Update submodules to main
run: |
git submodule foreach --quiet 'git checkout main && git pull --quiet origin main' 2>/dev/null || true
- name: Setup mokocli tools
env:
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
run: |
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
# Use pre-installed /opt/mokocli if available (updated by cron every 6h)
if [ -f /opt/mokocli/cli/version_bump.php ] && [ -f /opt/mokocli/cli/manifest_element.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then
echo Using pre-installed /opt/mokocli
echo MOKO_CLI=/opt/mokocli/cli >> $GITHUB_ENV
else
echo Falling back to fresh clone
if ! command -v composer > /dev/null 2>&1; then
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1
fi
rm -rf /tmp/mokocli
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/mokocli.git
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/mokocli
cd /tmp/mokocli && composer install --no-dev --no-interaction --quiet
echo MOKO_CLI=/tmp/mokocli/cli >> $GITHUB_ENV
fi
# Always fetch latest CLI tools — never use stale cache from previous runs
rm -rf /tmp/moko-platform-api
git clone --depth 1 --branch main --quiet \
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
/tmp/moko-platform-api
cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet
echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV"
- name: Detect platform
id: platform
run: |
# Auto-detect and update platform if not set in manifest
php ${MOKO_CLI}/platform_detect.php --path . --github-output 2>/dev/null || true
php ${MOKO_CLI}/manifest_read.php --path . --github-output
- name: Check platform eligibility (Joomla only)
id: eligibility
run: |
PLATFORM="${{ steps.platform.outputs.platform }}"
if [[ "$PLATFORM" == joomla* ]] || [[ "$PLATFORM" == "joomla" ]]; then
echo "proceed=true" >> "$GITHUB_OUTPUT"
else
echo "proceed=false" >> "$GITHUB_OUTPUT"
echo "::notice::Platform '$PLATFORM' — non-Joomla, skipping pre-release auto-bump"
fi
- name: Resolve metadata and bump version
id: meta
if: steps.eligibility.outputs.proceed == 'true'
run: |
# Auto-detect stability: RC for PRs targeting main, else use input or default to development
if [ "${{ github.event_name }}" = "pull_request_target" ] && [ "${{ github.event.pull_request.base.ref }}" = "main" ]; then
STABILITY="release-candidate"
# Auto-detect stability from branch name on push, or use input on dispatch
if [ "${{ github.event_name }}" = "push" ]; then
case "${{ github.ref_name }}" in
rc) STABILITY="release-candidate" ;;
alpha) STABILITY="alpha" ;;
beta) STABILITY="beta" ;;
*) STABILITY="development" ;;
esac
else
STABILITY="${{ inputs.stability || 'development' }}"
fi
@@ -96,20 +127,26 @@ jobs:
release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;;
esac
# Read current version (bump already handled by push workflow)
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null)
[ -z "$VERSION" ] && VERSION="00.00.01"
# Bump version via CLI: patch for dev/alpha/beta, minor for RC
case "$STABILITY" in
release-candidate) BUMP="minor" ;;
*) BUMP="patch" ;;
esac
# Strip any existing suffix from version before applying stability
php ${MOKO_CLI}/version_bump.php --path . $([ "$BUMP" = "minor" ] && echo "--minor") 2>/dev/null || true
# Set stability suffix and verify consistency
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "00.00.01")
VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//')
php ${MOKO_CLI}/version_set_platform.php \
--path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true
# Verify version consistency across all files
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
# Update VERSION variable with suffix
# Ensure licensing tags (updateservers, dlid) if enabled in manifest.xml
php ${MOKO_CLI}/manifest_licensing.php --path . --fix 2>/dev/null || true
# Append suffix for output
if [ -n "$SUFFIX" ]; then
VERSION="${VERSION}${SUFFIX}"
fi
@@ -146,6 +183,7 @@ jobs:
- name: Create release
id: release
if: steps.eligibility.outputs.proceed == 'true'
run: |
TAG="${{ steps.meta.outputs.tag }}"
VERSION="${{ steps.meta.outputs.version }}"
@@ -153,25 +191,47 @@ jobs:
php ${MOKO_CLI}/release_create.php \
--path . --version "$VERSION" --tag "$TAG" \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
--repo "${GITEA_REPO}" --branch dev --prerelease
--repo "${GITEA_REPO}" --branch "${{ github.ref_name }}" --prerelease
- name: Ensure prerelease flag
- name: Update release notes from CHANGELOG.md
if: steps.eligibility.outputs.proceed == 'true'
run: |
TAG="${{ steps.meta.outputs.tag }}"
VERSION="${{ steps.meta.outputs.version }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
# Get release ID by tag and force prerelease=true
RELEASE_ID=$(curl -s "${API_BASE}/releases/tags/${TAG}" \
-H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" | jq -r '.id // empty')
# Extract [Unreleased] section from changelog (everything between [Unreleased] and next ## heading)
if [ -f "CHANGELOG.md" ]; then
NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md)
[ -z "$NOTES" ] && NOTES="Release ${VERSION}"
else
NOTES="Release ${VERSION}"
fi
# Update release body via API
RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
"${API_BASE}/releases/tags/${TAG}" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
if [ -n "$RELEASE_ID" ]; then
curl -s -X PATCH "${API_BASE}/releases/${RELEASE_ID}" \
-H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
-H "Content-Type: application/json" \
-d '{"prerelease": true}'
echo "Marked release ${TAG} (id=${RELEASE_ID}) as prerelease"
python3 -c "
import json, urllib.request
body = open('/dev/stdin').read()
payload = json.dumps({'body': body}).encode()
req = urllib.request.Request(
'${API_BASE}/releases/${RELEASE_ID}',
data=payload, method='PATCH',
headers={
'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}',
'Content-Type': 'application/json'
})
urllib.request.urlopen(req)
" <<< "$NOTES"
echo "Release notes updated from CHANGELOG.md"
fi
- name: Build package and upload
id: package
if: steps.eligibility.outputs.proceed == 'true'
run: |
VERSION="${{ steps.meta.outputs.version }}"
TAG="${{ steps.meta.outputs.tag }}"
@@ -181,57 +241,11 @@ jobs:
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
--repo "${GITEA_REPO}" --output /tmp || true
- name: Update updates.xml
if: steps.platform.outputs.platform == 'joomla'
run: |
VERSION="${{ steps.meta.outputs.version }}"
STABILITY="${{ steps.meta.outputs.stability }}"
SHA256="${{ steps.package.outputs.sha256_zip }}"
if [ ! -f "updates.xml" ]; then
echo "No updates.xml -- skipping"
exit 0
fi
SHA_FLAG=""
[ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}"
php ${MOKO_CLI}/updates_xml_build.php \
--path . --version "${VERSION}" --stability "${STABILITY}" \
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
${SHA_FLAG}
# 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 push origin HEAD 2>&1 || echo "WARNING: push failed"
fi
- name: "Sync updates.xml to all branches"
if: steps.platform.outputs.platform == 'joomla'
run: |
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}" -- updates.xml 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
# updates.xml is generated dynamically by MokoGitea license server
# No need to build, commit, or sync updates.xml from workflows
- name: "Delete lesser pre-release channels (cascade)"
if: steps.eligibility.outputs.proceed == 'true'
continue-on-error: true
run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
+71
View File
@@ -0,0 +1,71 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Universal
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.mokogitea/workflows/rc-revert.yml
# VERSION: 09.23.00
# BRIEF: Rename rc/ branch back to dev/ when PR is closed without merge
name: "RC Revert"
on:
pull_request:
types: [closed]
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
revert:
name: Rename rc/ back to dev/
runs-on: ubuntu-latest
if: >-
github.event.pull_request.merged == false &&
startsWith(github.event.pull_request.head.ref, 'rc/')
steps:
- name: Rename branch
env:
BRANCH: ${{ github.event.pull_request.head.ref }}
REPO: ${{ github.repository }}
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
run: |
set -euo pipefail
# BRANCH is attacker-controlled (PR head ref). Strict allowlist before ANY use.
if ! printf '%s' "$BRANCH" | grep -Eq '^rc/[A-Za-z0-9._/-]+$'; then
echo "::error::Refusing unsafe branch name: $BRANCH"; exit 1
fi
SUFFIX="${BRANCH#rc/}"
DEV_BRANCH="dev/${SUFFIX}"
API="${GITEA_URL}/api/v1/repos/${REPO}/branches"
# Create dev/ branch from rc/ branch
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X POST \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"new_branch_name\": \"${DEV_BRANCH}\", \"old_branch_name\": \"${BRANCH}\"}" \
"${API}" 2>/dev/null || true)
if [ "$STATUS" = "201" ]; then
echo "Created branch: ${DEV_BRANCH}" >> "$GITHUB_STEP_SUMMARY"
else
echo "::error::Failed to create ${DEV_BRANCH} from ${BRANCH} (HTTP ${STATUS})"; exit 1
fi
# Read BRANCH from the environment inside PHP (getenv, no string interpolation -> no PHP injection)
ENCODED=$(php -r 'echo rawurlencode(getenv("BRANCH"));')
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \
-H "Authorization: token ${TOKEN}" \
"${API}/${ENCODED}" 2>/dev/null || true)
if [ "$STATUS" = "204" ]; then
echo "Deleted branch: ${BRANCH}" >> "$GITHUB_STEP_SUMMARY"
else
echo "::warning::Failed to delete ${BRANCH} (HTTP ${STATUS})"
fi
echo "### RC Reverted" >> "$GITHUB_STEP_SUMMARY"
echo "${BRANCH} → ${DEV_BRANCH}" >> "$GITHUB_STEP_SUMMARY"
File diff suppressed because it is too large Load Diff
-82
View File
@@ -1,82 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.Security
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
# PATH: /.gitea/workflows/security-audit.yml
# VERSION: 01.00.00
# BRIEF: Dependency vulnerability scanning for composer and npm packages
name: "Universal: Security Audit"
on:
schedule:
- cron: '0 6 * * 1' # Weekly on Monday at 06:00 UTC
pull_request:
branches:
- main
paths:
- 'composer.json'
- 'composer.lock'
- 'package.json'
- 'package-lock.json'
workflow_dispatch:
permissions:
contents: read
env:
NTFY_URL: ${{ vars.NTFY_URL || 'https://ntfy.mokoconsulting.tech' }}
NTFY_TOPIC: ${{ vars.NTFY_TOPIC || 'gitea-security' }}
jobs:
audit:
name: Dependency Audit
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Composer audit
if: hashFiles('composer.lock') != ''
run: |
echo "=== Composer Security Audit ==="
if ! command -v composer &> /dev/null; then
sudo apt-get update -qq
sudo apt-get install -y -qq php-cli composer >/dev/null 2>&1
fi
composer audit --format=plain 2>&1 | tee /tmp/composer-audit.txt
RESULT=$?
if [ $RESULT -ne 0 ]; then
echo "::warning::Composer vulnerabilities found"
echo "composer_vulnerable=true" >> "$GITHUB_ENV"
else
echo "No known vulnerabilities in composer dependencies"
fi
- name: NPM audit
if: hashFiles('package-lock.json') != ''
run: |
echo "=== NPM Security Audit ==="
npm audit --production 2>&1 | tee /tmp/npm-audit.txt || true
if npm audit --production 2>&1 | grep -q "found 0 vulnerabilities"; then
echo "No known vulnerabilities in npm dependencies"
else
echo "::warning::NPM vulnerabilities found"
echo "npm_vulnerable=true" >> "$GITHUB_ENV"
fi
- name: Notify on vulnerabilities
if: env.composer_vulnerable == 'true' || env.npm_vulnerable == 'true'
run: |
REPO="${{ github.event.repository.name }}"
curl -sS \
-H "Title: ${REPO} has vulnerable dependencies" \
-H "Tags: lock,warning" \
-H "Priority: high" \
-d "Security audit found vulnerabilities. Review dependency updates." \
"${NTFY_URL}/${NTFY_TOPIC}" || true
-312
View File
@@ -1,312 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Universal
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
# PATH: /templates/workflows/update-server.yml
# VERSION: 05.00.00
# BRIEF: Pre-release build + update server XML for dev/alpha/beta/rc branches
#
# Thin wrapper around moko-platform CLI tools.
# Builds packages, updates updates.xml, and optionally deploys via SFTP.
#
# Joomla filters update entries by the user's "Minimum Stability" setting.
name: "Update Server"
on:
push:
branches:
- 'dev'
- 'dev/**'
- 'alpha/**'
- 'beta/**'
- 'rc/**'
paths:
- 'src/**'
- 'htdocs/**'
pull_request:
types: [closed]
branches:
- 'dev'
- 'dev/**'
- 'alpha/**'
- 'beta/**'
- 'rc/**'
paths:
- 'src/**'
- 'htdocs/**'
workflow_dispatch:
inputs:
stability:
description: 'Stability tag'
required: true
default: 'development'
type: choice
options:
- development
- alpha
- beta
- rc
- stable
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
permissions:
contents: write
jobs:
update-xml:
name: Update Server
runs-on: release
if: >-
github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' || github.event_name == 'push'
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
token: ${{ secrets.MOKOGITEA_TOKEN }}
fetch-depth: 0
- name: Setup moko-platform tools
env:
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
COMPOSER_AUTH: '{"http-basic":{"git.mokoconsulting.tech":{"username":"token","password":"${{ secrets.MOKOGITEA_TOKEN }}"}}}'
run: |
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
# Always fetch latest CLI tools — never use stale cache from previous runs
rm -rf /tmp/moko-platform
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
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
echo "MOKO_CLI=/tmp/moko-platform/cli" >> "$GITHUB_ENV"
- name: Detect platform
id: platform
run: php ${MOKO_CLI}/manifest_read.php --path . --github-output
- name: Resolve stability and bump version
id: meta
run: |
BRANCH="${{ github.ref_name }}"
# Configure git for bot pushes
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]"
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
# Auto-bump patch version
php ${MOKO_CLI}/version_bump.php --path . 2>/dev/null || true
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "0.0.0")
# Strip any existing suffix before applying stability
VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//')
# Determine stability from branch or manual input
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
STABILITY="${{ inputs.stability }}"
elif [[ "$BRANCH" == rc/* ]]; then
STABILITY="rc"
elif [[ "$BRANCH" == beta/* ]]; then
STABILITY="beta"
elif [[ "$BRANCH" == alpha/* ]]; then
STABILITY="alpha"
else
STABILITY="development"
fi
# Version suffix per stability stream
case "$STABILITY" in
development) SUFFIX="-dev"; TAG="development" ;;
alpha) SUFFIX="-alpha"; TAG="alpha" ;;
beta) SUFFIX="-beta"; TAG="beta" ;;
rc) SUFFIX="-rc"; TAG="release-candidate" ;;
*) SUFFIX=""; TAG="stable" ;;
esac
# Propagate version with stability suffix to all manifest files
php ${MOKO_CLI}/version_set_platform.php \
--path . --version "$VERSION" --branch "$BRANCH" --stability "$STABILITY" 2>/dev/null || true
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
# Re-read version (now includes suffix from version_set_platform)
if [ -n "$SUFFIX" ]; then
VERSION="${VERSION}${SUFFIX}"
fi
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "display_version=${VERSION}" >> "$GITHUB_OUTPUT"
# Commit version bump if changed
git add -A
git diff --cached --quiet || {
git commit -m "chore(version): auto-bump ${VERSION} [skip ci]" \
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>"
git push
}
- name: Create release and upload package
id: package
run: |
VERSION="${{ steps.meta.outputs.version }}"
TAG="${{ steps.meta.outputs.tag }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
# Create or update Gitea release
php ${MOKO_CLI}/release_create.php \
--path . --version "$VERSION" --tag "$TAG" \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
--repo "${GITEA_REPO}" --branch "${{ github.ref_name }}" --prerelease
# Build package and upload
php ${MOKO_CLI}/release_package.php \
--path . --version "$VERSION" --tag "$TAG" \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
--repo "${GITEA_REPO}" --output /tmp || true
- name: Update updates.xml
if: steps.platform.outputs.platform == 'joomla'
run: |
VERSION="${{ steps.meta.outputs.version }}"
STABILITY="${{ steps.meta.outputs.stability }}"
SHA256="${{ steps.package.outputs.sha256_zip }}"
if [ ! -f "updates.xml" ]; then
echo "No updates.xml — skipping"
exit 0
fi
SHA_FLAG=""
[ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}"
php ${MOKO_CLI}/updates_xml_build.php \
--path . --version "${VERSION}" --stability "${STABILITY}" \
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
${SHA_FLAG}
# Commit and push updates.xml
git add updates.xml
git diff --cached --quiet || {
git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]"
git push
}
- name: Sync updates.xml to main
if: github.ref_name != 'main' && steps.platform.outputs.platform == 'joomla'
run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
GITEA_TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
FILE_SHA=$(curl -sf -H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/contents/updates.xml?ref=main" | python3 -c "import sys,json; print(json.load(sys.stdin).get('sha',''))" 2>/dev/null || true)
if [ -n "$FILE_SHA" ] && [ -f "updates.xml" ]; then
python3 -c "
import base64, json, urllib.request, sys
with open('updates.xml', 'rb') as f:
content = base64.b64encode(f.read()).decode()
payload = json.dumps({
'content': content,
'sha': '${FILE_SHA}',
'message': 'chore: sync updates.xml from ${{ steps.meta.outputs.stability }} [skip ci]',
'branch': 'main'
}).encode()
req = urllib.request.Request(
'${API_BASE}/contents/updates.xml',
data=payload, method='PUT',
headers={
'Authorization': 'token ${GITEA_TOKEN}',
'Content-Type': 'application/json'
})
try:
urllib.request.urlopen(req)
print('updates.xml synced to main')
except Exception as e:
print(f'WARNING: sync to main failed: {e}', file=sys.stderr)
"
fi
- name: SFTP deploy to dev server
if: contains(github.ref, 'dev/') || github.ref == 'refs/heads/dev'
env:
DEV_HOST: ${{ vars.DEV_FTP_HOST }}
DEV_PATH: ${{ vars.DEV_FTP_PATH }}
DEV_SUFFIX: ${{ vars.DEV_FTP_SUFFIX }}
DEV_USER: ${{ vars.DEV_FTP_USERNAME }}
DEV_PORT: ${{ vars.DEV_FTP_PORT }}
DEV_KEY: ${{ secrets.DEV_FTP_KEY }}
DEV_PASS: ${{ secrets.DEV_FTP_PASSWORD }}
run: |
# Permission check: admin or maintain role required
ACTOR="${{ github.actor }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
PERMISSION=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
"${API_BASE}/collaborators/${ACTOR}/permission" 2>/dev/null | \
python3 -c "import sys,json; print(json.load(sys.stdin).get('permission','read'))" 2>/dev/null || echo "read")
case "$PERMISSION" in
admin|maintain|write) ;;
*)
echo "Deploy denied: ${ACTOR} has '${PERMISSION}' — requires admin, maintain, or write"
exit 0
;;
esac
[ -z "$DEV_HOST" ] || [ -z "$DEV_PATH" ] && { echo "DEV FTP not configured — skipping SFTP"; exit 0; }
SOURCE_DIR="src"
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
[ ! -d "$SOURCE_DIR" ] && exit 0
PORT="${DEV_PORT:-22}"
REMOTE="${DEV_PATH%/}"
[ -n "$DEV_SUFFIX" ] && REMOTE="${REMOTE}/${DEV_SUFFIX#/}"
printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \
"$DEV_HOST" "$PORT" "$DEV_USER" "$REMOTE" > /tmp/sftp-config.json
if [ -n "$DEV_KEY" ]; then
echo "$DEV_KEY" > /tmp/deploy_key && chmod 600 /tmp/deploy_key
printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json
else
printf ',"password":"%s"}' "$DEV_PASS" >> /tmp/sftp-config.json
fi
PLATFORM=$(php ${MOKO_CLI}/platform_detect.php --path . 2>/dev/null || true)
if [ "$PLATFORM" = "waas-component" ] && [ -f "${MOKO_CLI}/../deploy/deploy-joomla.php" ]; then
php ${MOKO_CLI}/../deploy/deploy-joomla.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
elif [ -f "${MOKO_CLI}/../deploy/deploy-sftp.php" ]; then
php ${MOKO_CLI}/../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: Summary
if: always()
run: |
VERSION="${{ steps.meta.outputs.version }}"
STABILITY="${{ steps.meta.outputs.stability }}"
DISPLAY="${{ steps.meta.outputs.display_version }}"
echo "## Update Server" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Stability | \`${STABILITY}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Version | \`${DISPLAY}\` |" >> $GITHUB_STEP_SUMMARY
+130
View File
@@ -0,0 +1,130 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow.Template
# INGROUP: MokoStandards.CI
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla
# PATH: /.mokogitea/workflows/version-set.yml
# VERSION: 01.00.00
# BRIEF: Set or reset the extension version across all version-bearing files
name: "Joomla: Set Version"
on:
workflow_dispatch:
inputs:
version:
description: "Version number (e.g. 01.00.00)"
required: true
type: string
branch:
description: "Branch to update (default: current)"
required: false
type: string
permissions:
contents: write
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
set-version:
name: Set Version to ${{ inputs.version }}
runs-on: ubuntu-latest
steps:
- name: Validate version format
run: |
VERSION="${{ inputs.version }}"
if ! echo "$VERSION" | grep -qP '^\d{2}\.\d{2}\.\d{2}$'; then
echo "::error::Invalid version format '${VERSION}' — expected XX.YY.ZZ (e.g. 01.00.00)"
exit 1
fi
echo "VERSION=${VERSION}" >> "$GITHUB_ENV"
- name: Checkout
uses: actions/checkout@v4
with:
token: ${{ secrets.MOKOGITEA_TOKEN || github.token }}
ref: ${{ inputs.branch || github.ref }}
fetch-depth: 1
- name: Update manifest version
run: |
MANIFEST=""
for XML_FILE in $(find . -maxdepth 3 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*"); do
if grep -q "<extension" "$XML_FILE" 2>/dev/null; then
MANIFEST="$XML_FILE"
break
fi
done
if [ -z "$MANIFEST" ]; then
echo "::warning::No Joomla extension manifest found — skipping manifest update"
else
OLD_VER=$(grep -oP '<version>\K[^<]+' "$MANIFEST" | head -1)
sed -i "s|<version>${OLD_VER}</version>|<version>${VERSION}</version>|" "$MANIFEST"
echo "Manifest: ${OLD_VER} → ${VERSION} (${MANIFEST})"
fi
- name: Update README.md version
run: |
if [ -f "README.md" ]; then
if grep -qP '^\s*VERSION:\s*\d' README.md; then
sed -i -E "s/(VERSION:\s*)[0-9]{2}\.[0-9]{2}\.[0-9]{2}/\1${VERSION}/" README.md
echo "README.md version updated to ${VERSION}"
else
echo "::warning::No VERSION line found in README.md — skipping"
fi
fi
- name: Update CHANGELOG.md
run: |
if [ -f "CHANGELOG.md" ]; then
DATE=$(date +%Y-%m-%d)
# Check if this version already has an entry
if grep -q "^\#\# \[${VERSION}\]" CHANGELOG.md; then
echo "CHANGELOG.md already has entry for ${VERSION} — skipping"
else
# Insert new version entry after [Unreleased] or at the top after header
if grep -q '^\#\# \[Unreleased\]' CHANGELOG.md; then
sed -i "/^\#\# \[Unreleased\]/a\\\\n## [${VERSION}] --- ${DATE}" CHANGELOG.md
else
sed -i "/^\# Changelog/a\\\\n## [Unreleased]\n\n## [${VERSION}] --- ${DATE}" CHANGELOG.md
fi
echo "CHANGELOG.md: added entry for ${VERSION}"
fi
else
echo "::warning::No CHANGELOG.md found — skipping"
fi
- name: Update FILE INFORMATION blocks
run: |
# Update VERSION in file header blocks (# VERSION: XX.YY.ZZ)
find . -maxdepth 1 -type f \( -name "*.yml" -o -name "*.yaml" -o -name "*.php" -o -name "*.md" \) \
-not -path "./.git/*" -not -path "./vendor/*" -print0 2>/dev/null | \
while IFS= read -r -d '' FILE; do
if head -20 "$FILE" | grep -qP '^\s*#?\s*VERSION:\s*\d{2}\.\d{2}\.\d{2}'; then
sed -i -E "s/(#?\s*VERSION:\s*)[0-9]{2}\.[0-9]{2}\.[0-9]{2}/\1${VERSION}/" "$FILE"
echo "Updated FILE INFORMATION VERSION in ${FILE}"
fi
done
- name: Commit and push
run: |
git config user.name "Moko Consulting [bot]"
git config user.email "hello@mokoconsulting.tech"
git add -A
if git diff --cached --quiet; then
echo "No version changes detected — nothing to commit"
else
git commit -m "chore: set version to ${VERSION} [skip bump]
Authored-by: Moko Consulting"
git push
echo "### Version Set" >> $GITHUB_STEP_SUMMARY
echo "Version updated to \`${VERSION}\` on branch \`${GITHUB_REF_NAME}\`" >> $GITHUB_STEP_SUMMARY
fi
+3 -3
View File
@@ -11,9 +11,9 @@
You should have received a copy of the GNU General Public License (./LICENSE).
# FILE INFORMATION
DEFGROUP: MokoStandards-Template-Joomla-Plugin
INGROUP: MokoStandards-Template-Joomla-Plugin.Documentation
REPO: https://github.com/mokoconsulting-tech/MokoStandards-Template-Joomla-Plugin/
DEFGROUP: Template-Joomla
INGROUP: Template-Joomla.Documentation
REPO: https://github.com/mokoconsulting-tech/Template-Joomla/
VERSION: 01.01.00
PATH: ./CODE_OF_CONDUCT.md
BRIEF: Community expectations and enforcement guidelines
+7 -7
View File
@@ -16,12 +16,12 @@
You should have received a copy of the GNU General Public License (./LICENSE).
FILE INFORMATION
DEFGROUP: mokoconsulting-tech.MokoStandards-Template-Joomla-Plugin
DEFGROUP: mokoconsulting-tech.Template-Joomla
INGROUP: MokoStandards.Governance
REPO: https://github.com/mokoconsulting-tech/MokoStandards-Template-Joomla-Plugin
REPO: https://github.com/mokoconsulting-tech/Template-Joomla
VERSION: 01.01.00
PATH: /GOVERNANCE.md
BRIEF: Project governance rules, roles, and decision process for MokoStandards-Template-Joomla-Plugin
BRIEF: Project governance rules, roles, and decision process for Template-Joomla
-->
[![MokoStandards](https://img.shields.io/badge/MokoStandards-04.00.04-blue)](https://github.com/mokoconsulting-tech/MokoStandards)
@@ -30,7 +30,7 @@
## Overview
This document defines the governance model for the `MokoStandards-Template-Joomla-Plugin` repository within the
This document defines the governance model for the `Template-Joomla` repository within the
`mokoconsulting-tech` organization. It is automatically maintained by
[MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards) v04.00.04.
@@ -97,7 +97,7 @@ See the full policy:
## Reporting Issues
- **Bugs / Features**: Open a [GitHub Issue](https://github.com/mokoconsulting-tech/MokoStandards-Template-Joomla-Plugin/issues)
- **Bugs / Features**: Open a [GitHub Issue](https://github.com/mokoconsulting-tech/Template-Joomla/issues)
- **Security vulnerabilities**: See [SECURITY.md](./SECURITY.md)
- **Code of Conduct**: See [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md)
- **Contact**: dev@mokoconsulting.tech
@@ -110,10 +110,10 @@ See the full policy:
| ------------- | ----------------------------------------------- |
| Document Type | Policy |
| Domain | Governance |
| Applies To | mokoconsulting-tech/MokoStandards-Template-Joomla-Plugin |
| Applies To | mokoconsulting-tech/Template-Joomla |
| Jurisdiction | Tennessee, USA |
| Maintainer | @mokoconsulting-tech |
| Standards | MokoStandards v04.00.04 |
| Repo | https://github.com/mokoconsulting-tech/MokoStandards-Template-Joomla-Plugin |
| Repo | https://github.com/mokoconsulting-tech/Template-Joomla |
| Path | /GOVERNANCE.md |
| Status | Active — auto-maintained by MokoStandards |
-172
View File
@@ -1,172 +0,0 @@
# Makefile for Joomla Extensions
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
# SPDX-License-Identifier: GPL-3.0-or-later
#
# MokoJoomGallery — Photo gallery management for Joomla
# ==============================================================================
# CONFIGURATION - Customize these for your extension
# ==============================================================================
# Extension Configuration
EXTENSION_NAME := mokojoomgallery
EXTENSION_TYPE := package
# Options: module, plugin, component, package, template
EXTENSION_VERSION := 1.0.0
# Module Configuration (for modules only)
MODULE_TYPE := site
# Options: site, admin
# Plugin Configuration (for plugins only)
PLUGIN_GROUP := system
# Options: system, content, user, authentication, etc.
# Directories
SRC_DIR := src
BUILD_DIR := build
DIST_DIR := dist
DOCS_DIR := docs
# Joomla Installation (for local testing - customize paths)
JOOMLA_ROOT := /var/www/html/joomla
JOOMLA_VERSION := 4
# Tools
PHP := php
COMPOSER := composer
NPM := npm
PHPCS := vendor/bin/phpcs
PHPCBF := vendor/bin/phpcbf
PHPUNIT := vendor/bin/phpunit
ZIP := zip
# Coding Standards
PHPCS_STANDARD := Joomla
# Colors for output
COLOR_RESET := \033[0m
COLOR_GREEN := \033[32m
COLOR_YELLOW := \033[33m
COLOR_BLUE := \033[34m
COLOR_RED := \033[31m
# ==============================================================================
# TARGETS
# ==============================================================================
.PHONY: help
help: ## Show this help message
@echo "$(COLOR_BLUE)╔════════════════════════════════════════════════════════════╗$(COLOR_RESET)"
@echo "$(COLOR_BLUE)║ Joomla Extension Makefile ║$(COLOR_RESET)"
@echo "$(COLOR_BLUE)╚════════════════════════════════════════════════════════════╝$(COLOR_RESET)"
@echo ""
@echo "Extension: $(EXTENSION_NAME) ($(EXTENSION_TYPE)) v$(EXTENSION_VERSION)"
@echo ""
@echo "$(COLOR_GREEN)Available targets:$(COLOR_RESET)"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " $(COLOR_BLUE)%-20s$(COLOR_RESET) %s\n", $$1, $$2}'
@echo ""
.PHONY: install-deps
install-deps: ## Install all dependencies (Composer + npm)
@echo "$(COLOR_BLUE)Installing dependencies...$(COLOR_RESET)"
@if [ -f "composer.json" ]; then \
$(COMPOSER) install; \
echo "$(COLOR_GREEN)✓ Composer dependencies installed$(COLOR_RESET)"; \
fi
.PHONY: lint
lint: ## Run PHP linter (syntax check)
@echo "$(COLOR_BLUE)Running PHP linter...$(COLOR_RESET)"
@find . -name "*.php" ! -path "./vendor/*" ! -path "./node_modules/*" ! -path "./$(BUILD_DIR)/*" \
-exec $(PHP) -l {} \; | grep -v "No syntax errors" || true
@echo "$(COLOR_GREEN)✓ PHP linting complete$(COLOR_RESET)"
.PHONY: phpcs
phpcs: ## Run PHP CodeSniffer (Joomla standards)
@echo "$(COLOR_BLUE)Running PHP CodeSniffer...$(COLOR_RESET)"
@if [ -f "$(PHPCS)" ]; then \
$(PHPCS) --standard=$(PHPCS_STANDARD) --extensions=php --ignore=vendor,node_modules,$(BUILD_DIR) .; \
else \
echo "$(COLOR_YELLOW)⚠ PHP CodeSniffer not installed. Run: make install-deps$(COLOR_RESET)"; \
fi
.PHONY: validate
validate: lint phpcs ## Run all validation checks
@echo "$(COLOR_GREEN)✓ All validation checks passed$(COLOR_RESET)"
.PHONY: clean
clean: ## Clean build artifacts
@echo "$(COLOR_BLUE)Cleaning build artifacts...$(COLOR_RESET)"
@rm -rf $(BUILD_DIR) $(DIST_DIR)
@echo "$(COLOR_GREEN)✓ Build artifacts cleaned$(COLOR_RESET)"
MOKO_PLATFORM ?= $(or $(wildcard ../moko-platform),$(wildcard $(HOME)/moko-platform),$(wildcard /opt/moko-platform))
MINIFY_SCRIPT := $(MOKO_PLATFORM)/build/minify.js
.PHONY: minify
minify: ## Minify CSS/JS assets
@echo "Minifying assets..."
@if [ -f "$(MINIFY_SCRIPT)" ]; then \
node "$(MINIFY_SCRIPT)" $(SRC_DIR); \
elif [ -f "scripts/minify.js" ]; then \
node scripts/minify.js; \
else \
echo "No minify script found"; \
fi
.PHONY: build
build: clean minify ## Build extension package
@echo "$(COLOR_BLUE)Building Joomla package extension...$(COLOR_RESET)"
@mkdir -p $(DIST_DIR) $(BUILD_DIR)/packages
@# --- Build each sub-extension as a separate ZIP ---
@for EXT_DIR in $(SRC_DIR)/packages/*/; do \
EXT_NAME=$$(basename "$$EXT_DIR"); \
[ "$$EXT_NAME" = "index.html" ] && continue; \
echo " Packaging $$EXT_NAME..."; \
cd "$$EXT_DIR" && $(ZIP) -r "$(CURDIR)/$(BUILD_DIR)/packages/$${EXT_NAME}.zip" . \
-x "*.git*" -x "*/index.html" 2>/dev/null; \
cd "$(CURDIR)"; \
done
@# --- Build the outer package ZIP ---
@echo " Assembling pkg_$(EXTENSION_NAME)..."
@cp $(SRC_DIR)/pkg_mokojoomgallery.xml $(BUILD_DIR)/pkg_mokojoomgallery.xml
@cp $(SRC_DIR)/script.php $(BUILD_DIR)/script.php
@[ -d "$(SRC_DIR)/language" ] && cp -r $(SRC_DIR)/language $(BUILD_DIR)/language || true
@cd $(BUILD_DIR) && $(ZIP) -r "$(CURDIR)/$(DIST_DIR)/pkg_$(EXTENSION_NAME)-$(EXTENSION_VERSION).zip" \
pkg_mokojoomgallery.xml script.php language/ packages/
@echo "$(COLOR_GREEN)✓ Package created: $(DIST_DIR)/pkg_$(EXTENSION_NAME)-$(EXTENSION_VERSION).zip$(COLOR_RESET)"
@echo " Contents:"
@unzip -l "$(DIST_DIR)/pkg_$(EXTENSION_NAME)-$(EXTENSION_VERSION).zip" | tail -n +4 | head -20
.PHONY: package
package: build ## Alias for build
@echo "$(COLOR_GREEN)✓ Package ready for distribution$(COLOR_RESET)"
.PHONY: release
release: validate build ## Create a release (validate + build)
@echo "$(COLOR_GREEN)✓ Release package ready$(COLOR_RESET)"
.PHONY: version
version: ## Display version information
@echo "$(COLOR_BLUE)Extension Information:$(COLOR_RESET)"
@echo " Name: $(EXTENSION_NAME)"
@echo " Type: $(EXTENSION_TYPE)"
@echo " Version: $(EXTENSION_VERSION)"
.PHONY: security-check
security-check: ## Run security checks on dependencies
@echo "$(COLOR_BLUE)Running security checks...$(COLOR_RESET)"
@if [ -f "composer.json" ]; then \
$(COMPOSER) audit || echo "$(COLOR_YELLOW)⚠ Vulnerabilities found$(COLOR_RESET)"; \
fi
.PHONY: all
all: install-deps validate build ## Run complete build pipeline
@echo "$(COLOR_GREEN)✓ Complete build pipeline finished$(COLOR_RESET)"
# Default target
.DEFAULT_GOAL := help
+53 -50
View File
@@ -1,70 +1,73 @@
# MokoStandards-Template-Joomla
# Template-Joomla
Unified scaffolding templates for all Joomla extension types.
Unified scaffolding templates for all Joomla extension types — component, template, module, plugin, package, and library.
## Structure
```
.gitea/workflows/ ← Standard 10-workflow CI/CD suite
types/
plugin/ ← Joomla plugin scaffold
template/ ← Joomla site template scaffold (full src/)
module/ ← Joomla module scaffold
component/ ← Joomla component scaffold
package/ ← Joomla package scaffold
library/ ← Joomla library scaffold
.mokogitea/workflows/ ← CI/CD workflow suite (lint, validate, release)
samples/
manifest/ ← Reference manifest XML for each extension type
component_sample.xml
template_sample.xml
module_sample.xml
plugin_sample.xml
package_sample.xml
library_sample.xml
script/ ← Reference install/update script (script.php) templates
script_component.php
script_module.php
script_plugin.php
script_package.php
source/ ← Your extension source lives here (CI scans this dir)
```
## Usage
1. Create a new repo from this template (or clone manually)
2. Copy the relevant `types/<type>/` content to your project root
3. Customize the manifest XML, namespace, and source files
4. Workflows are already configured — push to trigger CI
1. Create a new repo from this template (or clone manually).
2. Build your extension under `source/`.
3. Copy the matching manifest from `samples/manifest/` to `source/`, rename it, and
replace the placeholder name, namespace, and version.
4. If your extension needs install/update logic, copy the matching
`samples/script/` file and replace the `{REPONAME}`/`{PACKAGENAME}` placeholders.
5. Push — the workflows validate the manifest, source, SQL, language keys, and build.
## Shared Files
## Samples
The root contains files shared by all extension types:
- `.gitea/workflows/` — CI/CD (auto-release, ci-joomla, pr-check, etc.)
- `.editorconfig`, `.gitignore`, `.gitattributes` — editor/git config
- `composer.json`, `phpstan.neon`, `Makefile` — build tooling
- `updates.xml` — Joomla update server manifest template
- `LICENSE`, `SECURITY.md`, `CONTRIBUTING.md` — standard docs
`samples/` holds read-only reference material — do not build against it directly.
## Workflow Suite (10 workflows)
- **`manifest/`** — a well-formed `<extension>` manifest for each of the six
Joomla extension types, showing the expected structure, namespace declaration,
media/SQL wiring, and update-server hookup.
- **`script/`** — the standard `script.php` install/uninstall/update handler
pattern shared across Moko extension repos. Copy, then replace `{REPONAME}`
(UPPERCASE) and `{PACKAGENAME}` (e.g. `com_myextension`).
| Workflow | Purpose |
|----------|---------|
| `auto-release.yml` | Stable release on PR merge to main |
| `pre-release.yml` | Manual dev/alpha/beta/rc builds |
| `ci-joomla.yml` | PHP lint, PHPStan, coding standards |
| `pr-check.yml` | PR gate: manifest + build validation |
| `deploy-manual.yml` | Manual SFTP deploy |
| `repo-health.yml` | Structure compliance checks |
| `update-server.yml` | updates.xml validation |
| `security-audit.yml` | Dependency vulnerability scanning |
| `notify.yml` | ntfy notifications |
| `cleanup.yml` | Merged branch + old run cleanup |
## CI/CD
## Version Policy
`.mokogitea/workflows/ci-joomla.yml` validates every push and PR against `source/`:
- **Stable** (PR merge to main): Minor bump (`03.00.07` -> `03.01.00`)
- **Pre-release** (manual): Patch bump (`03.00.07` -> `03.00.08`)
| Check | What it verifies |
|-------|------------------|
| Manifest | Well-formed XML, required elements, version |
| SQL | `#__` prefixes, `IF [NOT] EXISTS`, `ENGINE=InnoDB`, `utf8mb4`, install/uninstall pairing, version-named update files |
| Language keys | Keys used in code are defined; orphan (unused) keys flagged |
| PHP CodeSniffer | Joomla coding standard (falls back to PSR-12) |
| Security | No direct superglobals, raw SQL in `setQuery()`, `eval()`, or missing `_JEXEC` guard |
| updates.xml | Well-formed, required `<update>` children, `<targetplatform>` |
| Assets | `joomla.asset.json` is valid and its URIs exist |
| MVC naming | View/Controller/Model/Table classes follow suffix conventions |
| Router | Components ship a Router implementing the router interface |
| ACL | `access.xml` action keys are defined in language files |
| Webservices | Webservices plugins register API routes |
| ZIP dry-run | Every file/folder the manifest references actually exists |
| JS/CSS | Syntax + balanced braces (ESLint/Stylelint if configured) |
## Release Cascade
## Standards
Higher releases auto-delete lower pre-release channels:
- stable -> all | rc -> beta,alpha,dev | beta -> alpha,dev | alpha -> dev
## Platform Type
`.mokostandards` platform: `joomla-template` or `waas-component`
## Canonical Source
This repo is the **single source of truth** for Joomla workflows and scaffolding.
Sync to production repos by copying `.gitea/workflows/` from here.
This repo follows [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki/Home).
Documentation lives in the [Template-Joomla Wiki](https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla/wiki).
## License
GPL-3.0-or-later - Moko Consulting
GPL-3.0-or-later Moko Consulting
+5 -5
View File
@@ -19,9 +19,9 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
# FILE INFORMATION
DEFGROUP: MokoStandards-Template-Joomla-Plugin
INGROUP: MokoStandards-Template-Joomla-Plugin.Documentation
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards-Template-Joomla-Plugin
DEFGROUP: Template-Joomla
INGROUP: Template-Joomla.Documentation
REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla
PATH: /SECURITY.md
VERSION: 01.01.00
BRIEF: Security vulnerability reporting and handling policy
@@ -54,7 +54,7 @@ Report security vulnerabilities privately to:
**Email**: `security@mokoconsulting.tech`
**Subject Line**: `[SECURITY] MokoStandards-Template-Joomla-Plugin - Brief Description`
**Subject Line**: `[SECURITY] Template-Joomla - Brief Description`
### What to Include
@@ -228,7 +228,7 @@ The following are explicitly out of scope:
| ------------ | ------------------------------------------------------------------------------------------------------------ |
| Document | Security Policy |
| Path | /SECURITY.md |
| Repository | [https://github.com/mokoconsulting-tech/MokoStandards-Template-Joomla-Plugin](https://github.com/mokoconsulting-tech/MokoStandards-Template-Joomla-Plugin) |
| Repository | [https://github.com/mokoconsulting-tech/Template-Joomla](https://github.com/mokoconsulting-tech/Template-Joomla) |
| Owner | Moko Consulting |
| Scope | Security vulnerability handling |
| Status | Active |
-237
View File
@@ -1,237 +0,0 @@
#!/usr/bin/env bash
# ============================================================================
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Automation.CI
# INGROUP: moko-platform.Automation
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
# PATH: /automation/ci-issue-reporter.sh
# VERSION: 09.23.00
# BRIEF: Creates or updates a Gitea issue when a CI gate fails.
# Deduplicates by searching open issues with the "ci-auto" label
# whose title matches the gate. If a matching issue exists, a comment
# is appended instead of opening a duplicate.
# ============================================================================
set -euo pipefail
# ── Defaults ────────────────────────────────────────────────────────────────
GITEA_URL="${GITEA_URL:-https://git.mokoconsulting.tech}"
GITEA_TOKEN="${GITEA_TOKEN:-}"
REPO="${GITHUB_REPOSITORY:-}"
RUN_URL="${GITHUB_SERVER_URL:-${GITEA_URL}}/${REPO}/actions/runs/${GITHUB_RUN_ID:-0}"
LABEL_NAME="ci-auto"
LABEL_COLOR="#e11d48"
GATE=""
DETAILS=""
SEVERITY="error"
WORKFLOW=""
# ── Parse arguments ─────────────────────────────────────────────────────────
usage() {
cat <<EOF
Usage: ci-issue-reporter.sh --gate NAME --details TEXT [OPTIONS]
Required:
--gate CI gate name (e.g. "Code Quality", "Self-Health")
--details Human-readable failure description
Optional:
--severity "error" (default) or "warning"
--workflow Workflow name for the issue title
--repo owner/repo (default: \$GITHUB_REPOSITORY)
--run-url URL to the CI run (auto-detected from env)
--token Gitea API token (default: \$GITEA_TOKEN)
--url Gitea base URL (default: \$GITEA_URL)
EOF
exit 1
}
while [[ $# -gt 0 ]]; do
case "$1" in
--gate) GATE="$2"; shift 2 ;;
--details) DETAILS="$2"; shift 2 ;;
--severity) SEVERITY="$2"; shift 2 ;;
--workflow) WORKFLOW="$2"; shift 2 ;;
--repo) REPO="$2"; shift 2 ;;
--run-url) RUN_URL="$2"; shift 2 ;;
--token) GITEA_TOKEN="$2"; shift 2 ;;
--url) GITEA_URL="$2"; shift 2 ;;
-h|--help) usage ;;
*) echo "Unknown option: $1"; usage ;;
esac
done
[[ -z "$GATE" ]] && { echo "ERROR: --gate is required"; usage; }
[[ -z "$DETAILS" ]] && { echo "ERROR: --details is required"; usage; }
[[ -z "$GITEA_TOKEN" ]] && { echo "ERROR: GITEA_TOKEN not set"; exit 1; }
[[ -z "$REPO" ]] && { echo "ERROR: GITHUB_REPOSITORY not set"; exit 1; }
API="${GITEA_URL}/api/v1/repos/${REPO}"
# ── Build title ─────────────────────────────────────────────────────────────
if [[ -n "$WORKFLOW" ]]; then
TITLE="[CI] ${WORKFLOW}: ${GATE} failed"
else
TITLE="[CI] ${GATE} failed"
fi
# ── Ensure label exists ─────────────────────────────────────────────────────
ensure_label() {
local exists
exists=$(curl -sf -o /dev/null -w '%{http_code}' \
-H "Authorization: token ${GITEA_TOKEN}" \
"${API}/labels" 2>/dev/null || echo "000")
if [[ "$exists" == "200" ]]; then
# Check if label already exists
local found
found=$(curl -sf \
-H "Authorization: token ${GITEA_TOKEN}" \
"${API}/labels" 2>/dev/null \
| grep -o "\"name\":\"${LABEL_NAME}\"" || true)
if [[ -z "$found" ]]; then
curl -sf -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"${API}/labels" \
-d "{\"name\":\"${LABEL_NAME}\",\"color\":\"${LABEL_COLOR}\",\"description\":\"Auto-created by CI issue reporter\"}" \
> /dev/null 2>&1 || true
fi
fi
}
# ── Search for existing open issue ──────────────────────────────────────────
find_existing_issue() {
# URL-encode the gate name for the query
local query
query=$(printf '%s' "[CI] ${GATE}" | sed 's/ /%20/g; s/\[/%5B/g; s/\]/%5D/g')
local response
response=$(curl -sf \
-H "Authorization: token ${GITEA_TOKEN}" \
"${API}/issues?type=issues&state=open&labels=${LABEL_NAME}&q=${query}&limit=5" \
2>/dev/null || echo "[]")
# Extract the first matching issue number
echo "$response" \
| grep -oP '"number":\s*\K[0-9]+' \
| head -1
}
# ── Build issue body ────────────────────────────────────────────────────────
build_body() {
local severity_badge
if [[ "$SEVERITY" == "error" ]]; then
severity_badge="**Severity:** Error"
else
severity_badge="**Severity:** Warning"
fi
cat <<BODY
## CI Gate Failure: ${GATE}
${severity_badge}
**Workflow:** ${WORKFLOW:-unknown}
**Branch:** ${GITHUB_REF_NAME:-unknown}
**Commit:** \`${GITHUB_SHA:0:8}\`
**Run:** [View CI run](${RUN_URL})
### Details
${DETAILS}
### Resolution
Fix the issue described above and push a new commit. This issue will be closed automatically when the gate passes, or can be closed manually.
---
*Auto-created by [ci-issue-reporter](${GITEA_URL}/${REPO}/src/branch/main/automation/ci-issue-reporter.sh)*
BODY
}
# ── Build comment body (for existing issues) ────────────────────────────────
build_comment() {
cat <<COMMENT
### CI failure recurrence
**Branch:** ${GITHUB_REF_NAME:-unknown}
**Commit:** \`${GITHUB_SHA:0:8}\`
**Run:** [View CI run](${RUN_URL})
${DETAILS}
COMMENT
}
# ── Main ────────────────────────────────────────────────────────────────────
ensure_label
EXISTING=$(find_existing_issue)
if [[ -n "$EXISTING" ]]; then
# Append comment to existing issue
COMMENT_BODY=$(build_comment)
COMMENT_JSON=$(printf '%s' "$COMMENT_BODY" | python3 -c "
import sys, json
print(json.dumps({'body': sys.stdin.read()}))" 2>/dev/null)
HTTP=$(curl -sf -o /dev/null -w '%{http_code}' -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"${API}/issues/${EXISTING}/comments" \
-d "${COMMENT_JSON}" 2>/dev/null || echo "000")
if [[ "$HTTP" == "201" ]]; then
echo "Commented on existing issue #${EXISTING}"
else
echo "WARNING: Failed to comment on issue #${EXISTING} (HTTP ${HTTP})"
fi
else
# Create new issue
ISSUE_BODY=$(build_body)
ISSUE_JSON=$(python3 -c "
import sys, json
body = sys.stdin.read()
print(json.dumps({
'title': sys.argv[1],
'body': body,
'labels': []
}))" "$TITLE" <<< "$ISSUE_BODY" 2>/dev/null)
# Create the issue
RESPONSE=$(curl -sf -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"${API}/issues" \
-d "${ISSUE_JSON}" 2>/dev/null || echo "{}")
ISSUE_NUM=$(echo "$RESPONSE" | grep -oP '"number":\s*\K[0-9]+' | head -1)
if [[ -n "$ISSUE_NUM" ]]; then
# Apply label (separate call — more reliable across Gitea versions)
LABEL_ID=$(curl -sf \
-H "Authorization: token ${GITEA_TOKEN}" \
"${API}/labels" 2>/dev/null \
| grep -oP "\"id\":\s*\K[0-9]+(?=[^}]*\"name\":\s*\"${LABEL_NAME}\")" \
| head -1 || true)
if [[ -n "$LABEL_ID" ]]; then
curl -sf -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"${API}/issues/${ISSUE_NUM}/labels" \
-d "{\"labels\":[${LABEL_ID}]}" \
> /dev/null 2>&1 || true
fi
echo "Created issue #${ISSUE_NUM}: ${TITLE}"
else
echo "WARNING: Failed to create issue"
echo "Response: ${RESPONSE}"
fi
fi
+48
View File
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<extension type="component" method="upgrade">
<name>Moko Component</name>
<author>Moko Consulting</author>
<creationDate>July 2026</creationDate>
<copyright>Copyright (C) 2026 Moko Consulting. All rights reserved.</copyright>
<license>GNU General Public License version 3 or later; see LICENSE</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>1.0.0</version>
<description>A premium core business engine component engineered by Moko Consulting for Joomla 6 platform solutions.</description>
<namespace path="src">MokoConsulting\Component\MyExtension</namespace>
<files folder="site">
<folder>src</folder>
<folder>tmpl</folder>
</files>
<media destination="com_myextension" folder="media">
<filename>joomla.asset.json</filename>
<folder>css</folder>
<folder>js</folder>
</media>
<administration>
<menu link="option=com_myextension">Moko Component</menu>
<files folder="admin">
<folder>services</folder>
<folder>src</folder>
<folder>tmpl</folder>
<filename>myextension.xml</filename>
</files>
<languages folder="admin">
<language tag="en-GB">language/en-GB/com_myextension.ini</language>
<language tag="en-GB">language/en-GB/com_myextension.sys.ini</language>
<language tag="en-US">language/en-US/com_myextension.ini</language>
<language tag="en-US">language/en-US/com_myextension.sys.ini</language>
</languages>
</administration>
<updateservers>
<server type="extension" priority="1" name="Moko Consulting Updates" dlid="id">
https://update.mokoconsulting.tech/com_myextension.xml
</server>
</updateservers>
</extension>
+30
View File
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<extension type="library" method="upgrade">
<name>Moko Shared Core Library</name>
<libraryname>mokolibrary</libraryname>
<author>Moko Consulting</author>
<creationDate>July 2026</creationDate>
<copyright>Copyright (C) 2026 Moko Consulting. All rights reserved.</copyright>
<license>GNU General Public License version 3 or later; see LICENSE</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>1.0.0</version>
<description>Centralized helper framework and base API abstraction wrappers used across all active Moko Consulting extension matrices.</description>
<namespace path="src">MokoConsulting\Library\MokoLibrary</namespace>
<files>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/lib_mokolibrary.sys.ini</language>
<language tag="en-US">language/en-US/lib_mokolibrary.sys.ini</language>
</languages>
<updateservers>
<server type="extension" priority="1" name="Moko Consulting Updates" dlid="id">
https://update.mokoconsulting.tech/lib_mokolibrary.xml
</server>
</updateservers>
</extension>
+42
View File
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<extension type="module" client="site" method="upgrade">
<name>Moko Dynamic Content Showcase</name>
<author>Moko Consulting</author>
<creationDate>July 2026</creationDate>
<copyright>Copyright (C) 2026 Moko Consulting. All rights reserved.</copyright>
<license>GNU General Public License version 3 or later; see LICENSE</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>1.0.0</version>
<description>A high-performance asynchronous frontend display module designed to cleanly inject dynamic layout matrices into module zones.</description>
<namespace path="src">MokoConsulting\Module\MokoModule</namespace>
<files>
<folder module="mod_mokomodule">services</folder>
<folder>src</folder>
<folder>tmpl</folder>
<filename>mod_mokomodule.xml</filename>
</files>
<languages>
<language tag="en-GB">language/en-GB/mod_mokomodule.ini</language>
<language tag="en-GB">language/en-GB/mod_mokomodule.sys.ini</language>
<language tag="en-US">language/en-US/mod_mokomodule.ini</language>
<language tag="en-US">language/en-US/mod_mokomodule.sys.ini</language>
</languages>
<config>
<fields name="params">
<fieldset name="basic">
<field name="layout" type="modulelayout" label="Select Layout File" />
</fieldset>
</fields>
</config>
<updateservers>
<server type="extension" priority="1" name="Moko Consulting Updates" dlid="id">
https://update.mokoconsulting.tech/mod_mokomodule.xml
</server>
</updateservers>
</extension>
+32
View File
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<extension type="package" method="upgrade">
<name>Moko Full Suite Package</name>
<packagename>mokopackage</packagename>
<author>Moko Consulting</author>
<creationDate>July 2026</creationDate>
<copyright>Copyright (C) 2026 Moko Consulting. All rights reserved.</copyright>
<license>GNU General Public License version 3 or later; see LICENSE</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>1.0.0</version>
<description>All-in-one distribution bundle wrapping the Moko Component, Dynamic Showcase Module, and System Interceptor Plugin into a single installation step.</description>
<languages>
<language tag="en-GB">en-GB.pkg_mokopackage.sys.ini</language>
<language tag="en-US">en-US.pkg_mokopackage.sys.ini</language>
</languages>
<files folder="constituents">
<file type="component" id="com_myextension">com_myextension.zip</file>
<file type="module" id="mod_mokomodule" client="site">mod_mokomodule.zip</file>
<file type="plugin" id="mokoplugin" group="system">plg_system_mokoplugin.zip</file>
</files>
<updateservers>
<server type="collection" priority="1" name="Moko Consulting Updates" dlid="id">
https://update.mokoconsulting.tech/pkg_mokopackage.xml
</server>
</updateservers>
<blockChildUninstall>1</blockChildUninstall>
</extension>
+40
View File
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<extension type="plugin" group="system" method="upgrade">
<name>System - Moko Automation Plugin</name>
<author>Moko Consulting</author>
<creationDate>July 2026</creationDate>
<copyright>Copyright (C) 2026 Moko Consulting. All rights reserved.</copyright>
<license>GNU General Public License version 3 or later; see LICENSE</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>1.0.0</version>
<description>Advanced system-level interceptor framework developed by Moko Consulting for request optimizations in Joomla 6.</description>
<namespace path="src">MokoConsulting\Plugin\System\MokoPlugin</namespace>
<files>
<folder plugin="mokoplugin">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_system_mokoplugin.ini</language>
<language tag="en-GB">language/en-GB/plg_system_mokoplugin.sys.ini</language>
<language tag="en-US">language/en-US/plg_system_mokoplugin.ini</language>
<language tag="en-US">language/en-US/plg_system_mokoplugin.sys.ini</language>
</languages>
<config>
<fields name="params">
<fieldset name="basic">
<field name="example_param" type="text" label="API Endpoint Parameter" />
</fieldset>
</fields>
</config>
<updateservers>
<server type="extension" priority="1" name="Moko Consulting Updates" dlid="id">
https://update.mokoconsulting.tech/plg_system_mokoplugin.xml
</server>
</updateservers>
</extension>
+57
View File
@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<extension type="template" client="site" method="upgrade">
<name>tpl_mokotemplate</name>
<author>Moko Consulting</author>
<creationDate>July 2026</creationDate>
<copyright>Copyright (C) 2026 Moko Consulting. All rights reserved.</copyright>
<license>GNU General Public License version 3 or later; see LICENSE</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>1.0.0</version>
<description>Moko Corporate Template - A modern, high-performance responsive frontend design system for Joomla 6.</description>
<files>
<filename>templateDetails.xml</filename>
<filename>index.php</filename>
<filename>template_thumbnail.png</filename>
<filename>template_preview.png</filename>
<folder>css</folder>
<folder>js</folder>
<folder>images</folder>
<folder>html</folder>
<folder>language</folder>
</files>
<positions>
<position>topbar</position>
<position>header</position>
<position>menu</position>
<position>breadcrumbs</position>
<position>sidebar-left</position>
<position>sidebar-right</position>
<position>main-top</position>
<position>main-bottom</position>
<position>footer</position>
</positions>
<languages folder="language">
<language tag="en-GB">en-GB/tpl_mokotemplate.ini</language>
<language tag="en-GB">en-GB/tpl_mokotemplate.sys.ini</language>
<language tag="en-US">en-US/tpl_mokotemplate.ini</language>
<language tag="en-US">en-US/tpl_mokotemplate.sys.ini</language>
</languages>
<config>
<fields name="params">
<fieldset name="basic" label="Basic Parameters">
<field name="logo" type="media" label="Select Corporate Logo" />
</fieldset>
</fields>
</config>
<updateservers>
<server type="extension" priority="1" name="Moko Consulting Updates" dlid="id">
https://update.mokoconsulting.tech/tpl_mokotemplate.xml
</server>
</updateservers>
</extension>
+176
View File
@@ -0,0 +1,176 @@
<?php
/**
* ============================================================================
* INSTRUCTIONS FOR USE:
* 1. This file template serves as the standard structure for all script files
* across the extension repositories. Copy and implement this pattern
* universally in every script file to maintain consistency.
* 2. Replace '{REPONAME}' with the actual name of the repo (UPPERCASE).
* Example: "MOKOSUITEBACKUP"
* 3. Replace '{PACKAGENAME}' with the Joomla extension element name (lowercase).
* Example: "pkg_mokosuitebackup", "com_mokosuitebackup", "mod_mokosuitebackup"
* ============================================================================
*/
/**
* @package Joomla.Installer
* @subpackage {PACKAGENAME}
*/
use Joomla\CMS\Factory;
use Joomla\CMS\Log\Log;
defined('_JEXEC') || die;
class {REPONAME}InstallerScript
{
/**
* @var string The saved download key cached during preflight to survive updates
*/
private $savedDownloadKey = '';
/**
* Preflight hook runs before any installation/update/uninstallation action.
*/
public function preflight($type, $parent): bool
{
if ($type === 'update') {
$this->backupDownloadKey();
}
return true;
}
/**
* Postflight hook runs after any installation/update/uninstallation action.
*/
public function postflight($type, $parent): void
{
if ($type === 'install') {
$this->installSuccessful();
$this->warnMissingLicenseKey();
}
if ($type === 'update') {
$this->restoreDownloadKey();
$this->installSuccessful();
}
}
public function install($parent): void {}
public function update($parent): void {}
public function uninstall($parent): void {}
/**
* Cache the existing download key from the update sites table before update runs.
*/
private function backupDownloadKey(): void
{
try {
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select($db->quoteName('us.extra_query'))
->from($db->quoteName('#__update_sites', 'us'))
->join(
'INNER',
$db->quoteName('#__update_sites_extensions', 'use')
. ' ON ' . $db->quoteName('use.update_site_id') . ' = ' . $db->quoteName('us.update_site_id')
)
->join(
'INNER',
$db->quoteName('#__extensions', 'e')
. ' ON ' . $db->quoteName('e.extension_id') . ' = ' . $db->quoteName('use.extension_id')
)
->where($db->quoteName('e.element') . ' = ' . $db->quote('{PACKAGENAME}'))
->where($db->quoteName('e.type') . ' = ' . $db->quote('component'))
->setLimit(1);
$db->setQuery($query);
$extraQuery = (string) $db->loadResult();
if (!empty($extraQuery)) {
parse_str($extraQuery, $output);
$this->savedDownloadKey = $output['dlid'] ?? $extraQuery;
}
} catch (\Exception $e) {
Log::add('{REPONAME}: Could not backup download key: ' . $e->getMessage(), Log::WARNING, 'jerror');
}
}
/**
* Restore the download key to the (possibly new) update site record.
*/
private function restoreDownloadKey(): void
{
try {
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select($db->quoteName('us.update_site_id'))
->from($db->quoteName('#__update_sites', 'us'))
->join(
'INNER',
$db->quoteName('#__update_sites_extensions', 'use')
. ' ON ' . $db->quoteName('use.update_site_id') . ' = ' . $db->quoteName('us.update_site_id')
)
->join(
'INNER',
$db->quoteName('#__extensions', 'e')
. ' ON ' . $db->quoteName('e.extension_id') . ' = ' . $db->quoteName('use.extension_id')
)
->where($db->quoteName('e.element') . ' = ' . $db->quote('{PACKAGENAME}'))
->where($db->quoteName('e.type') . ' = ' . $db->quote('component'))
->setLimit(1);
$db->setQuery($query);
$updateSiteId = (int) $db->loadResult();
if ($updateSiteId > 0 && !empty($this->savedDownloadKey)) {
$query = $db->getQuery(true)
->update($db->quoteName('#__update_sites'))
->set($db->quoteName('extra_query') . ' = ' . $db->quote('dlid=' . $this->savedDownloadKey))
->where($db->quoteName('update_site_id') . ' = ' . $updateSiteId);
$db->setQuery($query);
$db->execute();
}
} catch (\Exception $e) {
Log::add('{REPONAME}: Could not restore download key: ' . $e->getMessage(), Log::WARNING, 'jerror');
Factory::getApplication()->enqueueMessage(
'<h4>{REPONAME}</h4>'
. '<p>Your download/license key could not be preserved during the update.</p>'
. '<p>Please re-enter it in the <a class="btn btn-sm btn-warning ms-2" href="index.php?option=com_installer&view=updatesites&filter[search]={PACKAGENAME}">Update Sites</a> manager to continue receiving updates.</p>',
'warning'
);
}
}
/**
* Show post-install license key prompt
*/
private function warnMissingLicenseKey(): void
{
try {
Factory::getApplication()->enqueueMessage(
'<h4>{REPONAME} License Key Required</h4>'
. '<p>A download/license key (DLID) is required to receive updates.</p>'
. '<p>Enter your key in the <a class="btn btn-sm btn-warning ms-2" href="index.php?option=com_installer&view=updatesites&filter[search]={PACKAGENAME}">Update Sites</a> manager '
. 'or contact <a class="btn btn-sm btn-warning ms-2" href="https://mokoconsulting.tech/support" target="_blank" rel="noopener">Moko Consulting Support</a> to obtain one.</p>',
'warning'
);
} catch (\Exception $e) {}
}
/**
* Show install successful prompt
*/
private function installSuccessful(): void
{
try {
Factory::getApplication()->enqueueMessage(
'<h4>{REPONAME} installed successfully!</h4>',
'info'
);
} catch (\Exception $e) {}
}
}
+176
View File
@@ -0,0 +1,176 @@
<?php
/**
* ============================================================================
* INSTRUCTIONS FOR USE:
* 1. This file template serves as the standard structure for all script files
* across the extension repositories. Copy and implement this pattern
* universally in every script file to maintain consistency.
* 2. Replace '{REPONAME}' with the actual name of the repo (UPPERCASE).
* Example: "MOKOSUITEBACKUP"
* 3. Replace '{PACKAGENAME}' with the Joomla extension element name (lowercase).
* Example: "pkg_mokosuitebackup", "com_mokosuitebackup", "mod_mokosuitebackup"
* ============================================================================
*/
/**
* @package Joomla.Installer
* @subpackage {PACKAGENAME}
*/
use Joomla\CMS\Factory;
use Joomla\CMS\Log\Log;
defined('_JEXEC') || die;
class {REPONAME}InstallerScript
{
/**
* @var string The saved download key cached during preflight to survive updates
*/
private $savedDownloadKey = '';
/**
* Preflight hook runs before any installation/update/uninstallation action.
*/
public function preflight($type, $parent): bool
{
if ($type === 'update') {
$this->backupDownloadKey();
}
return true;
}
/**
* Postflight hook runs after any installation/update/uninstallation action.
*/
public function postflight($type, $parent): void
{
if ($type === 'install') {
$this->installSuccessful();
$this->warnMissingLicenseKey();
}
if ($type === 'update') {
$this->restoreDownloadKey();
$this->installSuccessful();
}
}
public function install($parent): void {}
public function update($parent): void {}
public function uninstall($parent): void {}
/**
* Cache the existing download key from the update sites table before update runs.
*/
private function backupDownloadKey(): void
{
try {
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select($db->quoteName('us.extra_query'))
->from($db->quoteName('#__update_sites', 'us'))
->join(
'INNER',
$db->quoteName('#__update_sites_extensions', 'use')
. ' ON ' . $db->quoteName('use.update_site_id') . ' = ' . $db->quoteName('us.update_site_id')
)
->join(
'INNER',
$db->quoteName('#__extensions', 'e')
. ' ON ' . $db->quoteName('e.extension_id') . ' = ' . $db->quoteName('use.extension_id')
)
->where($db->quoteName('e.element') . ' = ' . $db->quote('{PACKAGENAME}'))
->where($db->quoteName('e.type') . ' = ' . $db->quote('module'))
->setLimit(1);
$db->setQuery($query);
$extraQuery = (string) $db->loadResult();
if (!empty($extraQuery)) {
parse_str($extraQuery, $output);
$this->savedDownloadKey = $output['dlid'] ?? $extraQuery;
}
} catch (\Exception $e) {
Log::add('{REPONAME}: Could not backup download key: ' . $e->getMessage(), Log::WARNING, 'jerror');
}
}
/**
* Restore the download key to the (possibly new) update site record.
*/
private function restoreDownloadKey(): void
{
try {
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select($db->quoteName('us.update_site_id'))
->from($db->quoteName('#__update_sites', 'us'))
->join(
'INNER',
$db->quoteName('#__update_sites_extensions', 'use')
. ' ON ' . $db->quoteName('use.update_site_id') . ' = ' . $db->quoteName('us.update_site_id')
)
->join(
'INNER',
$db->quoteName('#__extensions', 'e')
. ' ON ' . $db->quoteName('e.extension_id') . ' = ' . $db->quoteName('use.extension_id')
)
->where($db->quoteName('e.element') . ' = ' . $db->quote('{PACKAGENAME}'))
->where($db->quoteName('e.type') . ' = ' . $db->quote('module'))
->setLimit(1);
$db->setQuery($query);
$updateSiteId = (int) $db->loadResult();
if ($updateSiteId > 0 && !empty($this->savedDownloadKey)) {
$query = $db->getQuery(true)
->update($db->quoteName('#__update_sites'))
->set($db->quoteName('extra_query') . ' = ' . $db->quote('dlid=' . $this->savedDownloadKey))
->where($db->quoteName('update_site_id') . ' = ' . $updateSiteId);
$db->setQuery($query);
$db->execute();
}
} catch (\Exception $e) {
Log::add('{REPONAME}: Could not restore download key: ' . $e->getMessage(), Log::WARNING, 'jerror');
Factory::getApplication()->enqueueMessage(
'<h4>{REPONAME}</h4>'
. '<p>Your download/license key could not be preserved during the update.</p>'
. '<p>Please re-enter it in the <a class="btn btn-sm btn-warning ms-2" href="index.php?option=com_installer&view=updatesites&filter[search]={PACKAGENAME}">Update Sites</a> manager to continue receiving updates.</p>',
'warning'
);
}
}
/**
* Show post-install license key prompt
*/
private function warnMissingLicenseKey(): void
{
try {
Factory::getApplication()->enqueueMessage(
'<h4>{REPONAME} License Key Required</h4>'
. '<p>A download/license key (DLID) is required to receive updates.</p>'
. '<p>Enter your key in the <a class="btn btn-sm btn-warning ms-2" href="index.php?option=com_installer&view=updatesites&filter[search]={PACKAGENAME}">Update Sites</a> manager '
. 'or contact <a class="btn btn-sm btn-warning ms-2" href="https://mokoconsulting.tech/support" target="_blank" rel="noopener">Moko Consulting Support</a> to obtain one.</p>',
'warning'
);
} catch (\Exception $e) {}
}
/**
* Show install successful prompt
*/
private function installSuccessful(): void
{
try {
Factory::getApplication()->enqueueMessage(
'<h4>{REPONAME} installed successfully!</h4>',
'info'
);
} catch (\Exception $e) {}
}
}
+176
View File
@@ -0,0 +1,176 @@
<?php
/**
* ============================================================================
* INSTRUCTIONS FOR USE:
* 1. This file template serves as the standard structure for all script files
* across the extension repositories. Copy and implement this pattern
* universally in every script file to maintain consistency.
* 2. Replace '{REPONAME}' with the actual name of the repo (UPPERCASE).
* Example: "MOKOSUITEBACKUP"
* 3. Replace '{PACKAGENAME}' with the Joomla extension element name (lowercase).
* Example: "pkg_mokosuitebackup", "com_mokosuitebackup", "mod_mokosuitebackup"
* ============================================================================
*/
/**
* @package Joomla.Installer
* @subpackage {PACKAGENAME}
*/
use Joomla\CMS\Factory;
use Joomla\CMS\Log\Log;
defined('_JEXEC') || die;
class {REPONAME}InstallerScript
{
/**
* @var string The saved download key cached during preflight to survive updates
*/
private $savedDownloadKey = '';
/**
* Preflight hook runs before any installation/update/uninstallation action.
*/
public function preflight($type, $parent): bool
{
if ($type === 'update') {
$this->backupDownloadKey();
}
return true;
}
/**
* Postflight hook runs after any installation/update/uninstallation action.
*/
public function postflight($type, $parent): void
{
if ($type === 'install') {
$this->installSuccessful();
$this->warnMissingLicenseKey();
}
if ($type === 'update') {
$this->restoreDownloadKey();
$this->installSuccessful();
}
}
public function install($parent): void {}
public function update($parent): void {}
public function uninstall($parent): void {}
/**
* Cache the existing download key from the update sites table before update runs.
*/
private function backupDownloadKey(): void
{
try {
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select($db->quoteName('us.extra_query'))
->from($db->quoteName('#__update_sites', 'us'))
->join(
'INNER',
$db->quoteName('#__update_sites_extensions', 'use')
. ' ON ' . $db->quoteName('use.update_site_id') . ' = ' . $db->quoteName('us.update_site_id')
)
->join(
'INNER',
$db->quoteName('#__extensions', 'e')
. ' ON ' . $db->quoteName('e.extension_id') . ' = ' . $db->quoteName('use.extension_id')
)
->where($db->quoteName('e.element') . ' = ' . $db->quote('{PACKAGENAME}'))
->where($db->quoteName('e.type') . ' = ' . $db->quote('package'))
->setLimit(1);
$db->setQuery($query);
$extraQuery = (string) $db->loadResult();
if (!empty($extraQuery)) {
parse_str($extraQuery, $output);
$this->savedDownloadKey = $output['dlid'] ?? $extraQuery;
}
} catch (\Exception $e) {
Log::add('{REPONAME}: Could not backup download key: ' . $e->getMessage(), Log::WARNING, 'jerror');
}
}
/**
* Restore the download key to the (possibly new) update site record.
*/
private function restoreDownloadKey(): void
{
try {
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select($db->quoteName('us.update_site_id'))
->from($db->quoteName('#__update_sites', 'us'))
->join(
'INNER',
$db->quoteName('#__update_sites_extensions', 'use')
. ' ON ' . $db->quoteName('use.update_site_id') . ' = ' . $db->quoteName('us.update_site_id')
)
->join(
'INNER',
$db->quoteName('#__extensions', 'e')
. ' ON ' . $db->quoteName('e.extension_id') . ' = ' . $db->quoteName('use.extension_id')
)
->where($db->quoteName('e.element') . ' = ' . $db->quote('{PACKAGENAME}'))
->where($db->quoteName('e.type') . ' = ' . $db->quote('package'))
->setLimit(1);
$db->setQuery($query);
$updateSiteId = (int) $db->loadResult();
if ($updateSiteId > 0 && !empty($this->savedDownloadKey)) {
$query = $db->getQuery(true)
->update($db->quoteName('#__update_sites'))
->set($db->quoteName('extra_query') . ' = ' . $db->quote('dlid=' . $this->savedDownloadKey))
->where($db->quoteName('update_site_id') . ' = ' . $updateSiteId);
$db->setQuery($query);
$db->execute();
}
} catch (\Exception $e) {
Log::add('{REPONAME}: Could not restore download key: ' . $e->getMessage(), Log::WARNING, 'jerror');
Factory::getApplication()->enqueueMessage(
'<h4>{REPONAME}</h4>'
. '<p>Your download/license key could not be preserved during the update.</p>'
. '<p>Please re-enter it in the <a class="btn btn-sm btn-warning ms-2" href="index.php?option=com_installer&view=updatesites&filter[search]={PACKAGENAME}">Update Sites</a> manager to continue receiving updates.</p>',
'warning'
);
}
}
/**
* Show post-install license key prompt
*/
private function warnMissingLicenseKey(): void
{
try {
Factory::getApplication()->enqueueMessage(
'<h4>{REPONAME} License Key Required</h4>'
. '<p>A download/license key (DLID) is required to receive updates.</p>'
. '<p>Enter your key in the <a class="btn btn-sm btn-warning ms-2" href="index.php?option=com_installer&view=updatesites&filter[search]={PACKAGENAME}">Update Sites</a> manager '
. 'or contact <a class="btn btn-sm btn-warning ms-2" href="https://mokoconsulting.tech/support" target="_blank" rel="noopener">Moko Consulting Support</a> to obtain one.</p>',
'warning'
);
} catch (\Exception $e) {}
}
/**
* Show install successful prompt
*/
private function installSuccessful(): void
{
try {
Factory::getApplication()->enqueueMessage(
'<h4>{REPONAME} installed successfully!</h4>',
'info'
);
} catch (\Exception $e) {}
}
}
+176
View File
@@ -0,0 +1,176 @@
<?php
/**
* ============================================================================
* INSTRUCTIONS FOR USE:
* 1. This file template serves as the standard structure for all script files
* across the extension repositories. Copy and implement this pattern
* universally in every script file to maintain consistency.
* 2. Replace '{REPONAME}' with the actual name of the repo (UPPERCASE).
* Example: "MOKOSUITEBACKUP"
* 3. Replace '{PACKAGENAME}' with the Joomla extension element name (lowercase).
* Example: "pkg_mokosuitebackup", "com_mokosuitebackup", "mod_mokosuitebackup"
* ============================================================================
*/
/**
* @package Joomla.Installer
* @subpackage {PACKAGENAME}
*/
use Joomla\CMS\Factory;
use Joomla\CMS\Log\Log;
defined('_JEXEC') || die;
class {REPONAME}InstallerScript
{
/**
* @var string The saved download key cached during preflight to survive updates
*/
private $savedDownloadKey = '';
/**
* Preflight hook runs before any installation/update/uninstallation action.
*/
public function preflight($type, $parent): bool
{
if ($type === 'update') {
$this->backupDownloadKey();
}
return true;
}
/**
* Postflight hook runs after any installation/update/uninstallation action.
*/
public function postflight($type, $parent): void
{
if ($type === 'install') {
$this->installSuccessful();
$this->warnMissingLicenseKey();
}
if ($type === 'update') {
$this->restoreDownloadKey();
$this->installSuccessful();
}
}
public function install($parent): void {}
public function update($parent): void {}
public function uninstall($parent): void {}
/**
* Cache the existing download key from the update sites table before update runs.
*/
private function backupDownloadKey(): void
{
try {
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select($db->quoteName('us.extra_query'))
->from($db->quoteName('#__update_sites', 'us'))
->join(
'INNER',
$db->quoteName('#__update_sites_extensions', 'use')
. ' ON ' . $db->quoteName('use.update_site_id') . ' = ' . $db->quoteName('us.update_site_id')
)
->join(
'INNER',
$db->quoteName('#__extensions', 'e')
. ' ON ' . $db->quoteName('e.extension_id') . ' = ' . $db->quoteName('use.extension_id')
)
->where($db->quoteName('e.element') . ' = ' . $db->quote('{PACKAGENAME}'))
->where($db->quoteName('e.type') . ' = ' . $db->quote('plugin'))
->setLimit(1);
$db->setQuery($query);
$extraQuery = (string) $db->loadResult();
if (!empty($extraQuery)) {
parse_str($extraQuery, $output);
$this->savedDownloadKey = $output['dlid'] ?? $extraQuery;
}
} catch (\Exception $e) {
Log::add('{REPONAME}: Could not backup download key: ' . $e->getMessage(), Log::WARNING, 'jerror');
}
}
/**
* Restore the download key to the (possibly new) update site record.
*/
private function restoreDownloadKey(): void
{
try {
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select($db->quoteName('us.update_site_id'))
->from($db->quoteName('#__update_sites', 'us'))
->join(
'INNER',
$db->quoteName('#__update_sites_extensions', 'use')
. ' ON ' . $db->quoteName('use.update_site_id') . ' = ' . $db->quoteName('us.update_site_id')
)
->join(
'INNER',
$db->quoteName('#__extensions', 'e')
. ' ON ' . $db->quoteName('e.extension_id') . ' = ' . $db->quoteName('use.extension_id')
)
->where($db->quoteName('e.element') . ' = ' . $db->quote('{PACKAGENAME}'))
->where($db->quoteName('e.type') . ' = ' . $db->quote('plugin'))
->setLimit(1);
$db->setQuery($query);
$updateSiteId = (int) $db->loadResult();
if ($updateSiteId > 0 && !empty($this->savedDownloadKey)) {
$query = $db->getQuery(true)
->update($db->quoteName('#__update_sites'))
->set($db->quoteName('extra_query') . ' = ' . $db->quote('dlid=' . $this->savedDownloadKey))
->where($db->quoteName('update_site_id') . ' = ' . $updateSiteId);
$db->setQuery($query);
$db->execute();
}
} catch (\Exception $e) {
Log::add('{REPONAME}: Could not restore download key: ' . $e->getMessage(), Log::WARNING, 'jerror');
Factory::getApplication()->enqueueMessage(
'<h4>{REPONAME}</h4>'
. '<p>Your download/license key could not be preserved during the update.</p>'
. '<p>Please re-enter it in the <a class="btn btn-sm btn-warning ms-2" href="index.php?option=com_installer&view=updatesites&filter[search]={PACKAGENAME}">Update Sites</a> manager to continue receiving updates.</p>',
'warning'
);
}
}
/**
* Show post-install license key prompt
*/
private function warnMissingLicenseKey(): void
{
try {
Factory::getApplication()->enqueueMessage(
'<h4>{REPONAME} License Key Required</h4>'
. '<p>A download/license key (DLID) is required to receive updates.</p>'
. '<p>Enter your key in the <a class="btn btn-sm btn-warning ms-2" href="index.php?option=com_installer&view=updatesites&filter[search]={PACKAGENAME}">Update Sites</a> manager '
. 'or contact <a class="btn btn-sm btn-warning ms-2" href="https://mokoconsulting.tech/support" target="_blank" rel="noopener">Moko Consulting Support</a> to obtain one.</p>',
'warning'
);
} catch (\Exception $e) {}
}
/**
* Show install successful prompt
*/
private function installSuccessful(): void
{
try {
Factory::getApplication()->enqueueMessage(
'<h4>{REPONAME} installed successfully!</h4>',
'info'
);
} catch (\Exception $e) {}
}
}
-3
View File
@@ -1,3 +0,0 @@
# Component Scaffold
Copy this directory as the starting point for a new Joomla component.
-3
View File
@@ -1,3 +0,0 @@
# Library Scaffold
Copy this directory as the starting point for a new Joomla library.
View File
-3
View File
@@ -1,3 +0,0 @@
# Module Scaffold
Copy this directory as the starting point for a new Joomla module.
View File
-3
View File
@@ -1,3 +0,0 @@
# Package Scaffold
Copy this directory as the starting point for a new Joomla package.
View File
-3
View File
@@ -1,3 +0,0 @@
# Plugin Scaffold
Copy this directory as the starting point for a new Joomla plugin.
View File
-4
View File
@@ -1,4 +0,0 @@
# Template Scaffold
Copy this directory as the starting point for a new Joomla site template.
Includes: index.php, error.php, offline.php, templateDetails.xml, css/, js/, fonts/, images/, html/
-13
View File
@@ -1,13 +0,0 @@
/**
* Custom CSS for yourtemplate
*
* This file is for user customizations.
* It will not be overwritten during template updates.
*
* @package Joomla.Site
* @subpackage Templates.yourtemplate
* @copyright Copyright (C) 2026. All rights reserved.
* @license GNU General Public License version 2 or later
*/
/* Add your custom styles below this line */
-591
View File
@@ -1,591 +0,0 @@
/**
* Template CSS for yourtemplate
*
* This file contains all template styles in one place for simplicity.
* For larger projects, consider splitting into modular files:
* - base.css (resets, typography, base elements)
* - layout.css (grid, containers, responsive)
* - components.css (buttons, forms, alerts)
* - utilities.css (helper classes)
*
* Alternatively, use CSS preprocessing (Sass, Less, PostCSS) to organize
* code into partial files that compile into a single output file.
*
* @package Joomla.Site
* @subpackage Templates.yourtemplate
* @copyright Copyright (C) 2026. All rights reserved.
* @license GNU General Public License version 2 or later
*/
/* ==========================================================================
CSS Custom Properties (Variables)
========================================================================== */
:root {
/* Colors */
--color-primary: #0066cc;
--color-primary-dark: #0052a3;
--color-text: #333;
--color-text-light: #666;
--color-background: #fff;
--color-background-light: #f9f9f9;
--color-background-gray: #f5f5f5;
--color-border: #e0e0e0;
--color-error: #d9534f;
--color-success: #5cb85c;
--color-warning: #f0ad4e;
--color-info: #5bc0de;
/* Typography */
--font-family-base: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
--font-size-base: 1rem;
--line-height-base: 1.6;
/* Spacing */
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 1.5rem;
--spacing-xl: 3rem;
/* Layout */
--container-max-width: 1200px;
--border-radius: 4px;
--box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
/* ==========================================================================
Base Styles
========================================================================== */
* {
box-sizing: border-box;
}
html {
font-size: 16px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
margin: 0;
padding: 0;
font-family: var(--font-family-base);
font-size: var(--font-size-base);
line-height: var(--line-height-base);
color: var(--color-text);
background-color: var(--color-background);
}
/* ==========================================================================
Layout Containers
========================================================================== */
.container {
width: 100%;
max-width: var(--container-max-width);
margin: 0 auto;
padding: 0 15px;
}
.container-fluid {
width: 100%;
padding: 0 15px;
}
.row {
display: flex;
flex-wrap: wrap;
margin: 0 -15px;
}
[class*="col-"] {
padding: 0 15px;
width: 100%;
}
/* ==========================================================================
Grid System
========================================================================== */
@media (min-width: 768px) {
.col-md-3 {
width: 25%;
}
.col-md-6 {
width: 50%;
}
.col-md-9 {
width: 75%;
}
.col-md-12 {
width: 100%;
}
}
/* ==========================================================================
Header Styles
========================================================================== */
.site-header {
background: var(--color-background);
border-bottom: 1px solid var(--color-border);
padding: 20px 0;
}
.site-header.sticky-header {
position: sticky;
top: 0;
z-index: 1000;
box-shadow: var(--box-shadow);
}
.header-inner {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
}
.site-branding {
flex: 0 0 auto;
}
.site-logo {
max-height: 60px;
width: auto;
display: block;
}
.site-title {
margin: 0;
font-size: 1.75rem;
font-weight: 700;
}
.site-title a {
color: var(--color-text);
text-decoration: none;
}
.site-title a:hover {
color: var(--color-primary);
}
.site-description {
margin-top: 5px;
font-size: 0.875rem;
color: var(--color-text-light);
}
.header-modules {
flex: 1 1 auto;
text-align: right;
}
/* ==========================================================================
Top Bar
========================================================================== */
.top-bar {
background: var(--color-background-gray);
padding: 10px 0;
font-size: 0.875rem;
}
/* ==========================================================================
Navigation Styles
========================================================================== */
.site-navigation {
background: var(--color-primary);
padding: 0;
}
.site-navigation ul {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-wrap: wrap;
}
.site-navigation li {
position: relative;
}
.site-navigation a {
display: block;
padding: 15px 20px;
color: #fff;
text-decoration: none;
transition: background-color 0.3s ease;
}
.site-navigation a:hover,
.site-navigation a:focus {
background-color: rgba(0, 0, 0, 0.1);
}
.site-navigation .active > a {
background-color: rgba(0, 0, 0, 0.2);
}
/* ==========================================================================
Banner Area
========================================================================== */
.banner-area {
padding: 40px 0;
background: var(--color-background-light);
}
/* ==========================================================================
Main Content Area
========================================================================== */
.site-main {
padding: 40px 0;
min-height: 400px;
}
.content-area {
flex: 1 1 auto;
}
.main-content {
margin: 20px 0;
}
.main-top,
.main-bottom {
margin: 20px 0;
}
/* ==========================================================================
Sidebar Styles
========================================================================== */
.sidebar {
padding: 20px 0;
}
.sidebar .module {
margin-bottom: 30px;
padding: 20px;
background: var(--color-background-light);
border-radius: var(--border-radius);
}
.sidebar .module h3 {
margin-top: 0;
margin-bottom: 15px;
font-size: 1.25rem;
border-bottom: 2px solid var(--color-primary);
padding-bottom: 10px;
}
/* ==========================================================================
Footer Styles
========================================================================== */
.site-footer {
background: var(--color-text);
color: var(--color-background);
padding: 40px 0 20px;
}
.site-footer a {
color: var(--color-background);
text-decoration: none;
}
.site-footer a:hover {
color: var(--color-primary);
text-decoration: underline;
}
/* ==========================================================================
Error Page Styles
========================================================================== */
.site-error {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
background: var(--color-background-gray);
}
.error-container {
max-width: 600px;
padding: 40px;
}
.error-content {
background: var(--color-background);
padding: 40px;
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
text-align: center;
}
.error-logo {
max-height: 80px;
margin-bottom: 20px;
}
.error-message h2 {
color: var(--color-error);
margin-top: 0;
}
.error-actions {
margin-top: 30px;
}
.error-technical {
margin-top: 30px;
padding: 20px;
background: var(--color-background-light);
border-radius: var(--border-radius);
text-align: left;
font-size: 0.875rem;
}
/* ==========================================================================
Offline Page Styles
========================================================================== */
.site-offline {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
background: var(--color-background-gray);
}
.offline-container {
max-width: 500px;
padding: 40px;
}
.offline-content {
background: var(--color-background);
padding: 40px;
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
text-align: center;
}
.offline-logo {
max-height: 80px;
margin-bottom: 20px;
}
/* ==========================================================================
Buttons
========================================================================== */
.btn {
display: inline-block;
padding: 10px 20px;
font-size: 1rem;
font-weight: 400;
text-align: center;
text-decoration: none;
border: 1px solid transparent;
border-radius: var(--border-radius);
cursor: pointer;
transition: all 0.3s ease;
}
.btn-primary {
background-color: var(--color-primary);
border-color: var(--color-primary);
color: var(--color-background);
}
.btn-primary:hover {
background-color: var(--color-primary-dark);
border-color: var(--color-primary-dark);
color: var(--color-background);
}
/* ==========================================================================
Messages
========================================================================== */
.alert {
padding: 15px;
margin-bottom: 20px;
border: 1px solid transparent;
border-radius: var(--border-radius);
}
.alert-success {
background-color: #d4edda;
border-color: #c3e6cb;
color: #155724;
}
.alert-info {
background-color: #d1ecf1;
border-color: #bee5eb;
color: #0c5460;
}
.alert-warning {
background-color: #fff3cd;
border-color: #ffeaa7;
color: #856404;
}
.alert-error,
.alert-danger {
background-color: #f8d7da;
border-color: #f5c6cb;
color: #721c24;
}
/* ==========================================================================
Typography
========================================================================== */
h1, h2, h3, h4, h5, h6 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 600;
line-height: 1.2;
}
h1 { font-size: 2.5rem; }
h2 { font-size: 2rem; }
h3 { font-size: 1.75rem; }
h4 { font-size: 1.5rem; }
h5 { font-size: 1.25rem; }
h6 { font-size: 1rem; }
p {
margin-top: 0;
margin-bottom: 1rem;
}
a {
color: var(--color-primary);
text-decoration: none;
}
a:hover {
color: var(--color-primary-dark);
text-decoration: underline;
}
/* ==========================================================================
Responsive Styles
========================================================================== */
@media (max-width: 767px) {
.header-inner {
flex-direction: column;
text-align: center;
}
.header-modules {
text-align: center;
margin-top: 15px;
}
/*
* Mobile navigation styling
* Note: This provides basic stacking for mobile. For a toggle menu,
* add a .mobile-menu-toggle button and implement show/hide functionality.
* See template.js for the toggle handler.
*/
.site-navigation ul {
flex-direction: column;
}
.site-navigation a {
padding: 12px 15px;
}
/* Mobile menu toggle button (add to your template HTML) */
.mobile-menu-toggle {
display: inline-block;
padding: 10px;
background: transparent;
border: none;
cursor: pointer;
font-size: 1.5rem;
}
/* Mobile menu active state */
.site-navigation.mobile-menu-active {
display: block;
}
.row {
flex-direction: column;
}
[class*="col-"] {
width: 100%;
}
}
/* ==========================================================================
Accessibility
========================================================================== */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
a:focus,
button:focus {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
/* ==========================================================================
Utilities
========================================================================== */
.text-center {
text-align: center;
}
.text-right {
text-align: right;
}
.text-left {
text-align: left;
}
.mt-0 { margin-top: 0; }
.mt-1 { margin-top: 0.25rem; }
.mt-2 { margin-top: 0.5rem; }
.mt-3 { margin-top: 1rem; }
.mt-4 { margin-top: 1.5rem; }
.mt-5 { margin-top: 3rem; }
.mb-0 { margin-bottom: 0; }
.mb-1 { margin-bottom: 0.25rem; }
.mb-2 { margin-bottom: 0.5rem; }
.mb-3 { margin-bottom: 1rem; }
.mb-4 { margin-bottom: 1.5rem; }
.mb-5 { margin-bottom: 3rem; }
-93
View File
@@ -1,93 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage Templates.yourtemplate
*
* @copyright Copyright (C) 2026. All rights reserved.
* @license GNU General Public License version 2 or later
*/
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Uri\Uri;
/** @var Joomla\CMS\Document\ErrorDocument $this */
$app = Factory::getApplication();
$wa = $this->getWebAssetManager();
// Template params
$logo = $this->params->get('logo');
$sitename = htmlspecialchars($app->get('sitename'), ENT_QUOTES, 'UTF-8');
// Add template CSS
$wa->registerAndUseStyle(
'template.yourtemplate',
'templates/' . $this->template . '/css/template.css',
[],
['version' => 'auto']
);
?>
<!DOCTYPE html>
<html lang="<?php echo $this->language; ?>" dir="<?php echo $this->direction; ?>">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo $this->title; ?> - <?php echo $sitename; ?></title>
<jdoc:include type="styles" />
<jdoc:include type="scripts" />
</head>
<body class="site-error">
<div class="error-container">
<div class="error-content">
<?php if ($logo) : ?>
<a href="<?php echo Uri::root(); ?>">
<img src="<?php echo Uri::root() . htmlspecialchars($logo, ENT_QUOTES, 'UTF-8'); ?>" alt="<?php echo $sitename; ?>" class="error-logo">
</a>
<?php else : ?>
<h1 class="site-title">
<a href="<?php echo Uri::root(); ?>"><?php echo $sitename; ?></a>
</h1>
<?php endif; ?>
<div class="error-message">
<h2><?php echo Text::_('JERROR_LAYOUT_PAGE_NOT_FOUND'); ?></h2>
<div class="error-details">
<p><strong><?php echo Text::_('JERROR_LAYOUT_ERROR_HAS_OCCURRED_WHILE_PROCESSING_YOUR_REQUEST'); ?></strong></p>
<?php if ($this->debug) : ?>
<div class="error-debug">
<p><?php echo Text::_('JERROR_LAYOUT_NOT_ABLE_TO_VISIT'); ?></p>
<ul>
<li><?php echo Text::_('JERROR_LAYOUT_AN_OUT_OF_DATE_BOOKMARK_FAVOURITE'); ?></li>
<li><?php echo Text::_('JERROR_LAYOUT_MIS_TYPED_ADDRESS'); ?></li>
<li><?php echo Text::_('JERROR_LAYOUT_SEARCH_ENGINE_OUT_OF_DATE_LISTING'); ?></li>
<li><?php echo Text::_('JERROR_LAYOUT_YOU_HAVE_NO_ACCESS_TO_THIS_PAGE'); ?></li>
</ul>
</div>
<?php endif; ?>
</div>
</div>
<div class="error-actions">
<a href="<?php echo Uri::root(); ?>" class="btn btn-primary">
<?php echo Text::_('JERROR_LAYOUT_GO_TO_THE_HOME_PAGE'); ?>
</a>
</div>
<?php if ($this->debug) : ?>
<div class="error-technical">
<h3><?php echo Text::_('JERROR_LAYOUT_TECHNICAL_INFORMATION'); ?></h3>
<p><?php echo $this->error->getMessage(); ?></p>
<p>
<?php if ($this->error->getCode()) : ?>
<?php echo Text::_('JERROR_LAYOUT_ERROR_CODE'); ?>: <?php echo $this->error->getCode(); ?>
<?php endif; ?>
</p>
</div>
<?php endif; ?>
</div>
</div>
</body>
</html>
-183
View File
@@ -1,183 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage Templates.yourtemplate
*
* @copyright Copyright (C) 2026. All rights reserved.
* @license GNU General Public License version 2 or later
*/
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Uri\Uri;
/** @var Joomla\CMS\Document\HtmlDocument $this */
$app = Factory::getApplication();
$wa = $this->getWebAssetManager();
// Detecting Active Variables
$option = $app->input->getCmd('option', '');
$view = $app->input->getCmd('view', '');
$layout = $app->input->getCmd('layout', '');
$task = $app->input->getCmd('task', '');
$itemid = $app->input->getCmd('Itemid', '');
$sitename = htmlspecialchars($app->get('sitename'), ENT_QUOTES, 'UTF-8');
$menu = $app->getMenu()->getActive();
$pageclass = $menu !== null ? $menu->getParams()->get('pageclass_sfx', '') : '';
// Template params
$logo = $this->params->get('logo');
$siteTitle = $this->params->get('siteTitle', $sitename);
$siteDescription = $this->params->get('siteDescription', '');
$containerFluid = (bool) $this->params->get('containerFluid', false);
$stickyHeader = (bool) $this->params->get('stickyHeader', false);
// Add template CSS
$wa->registerAndUseStyle(
'template.yourtemplate',
'templates/' . $this->template . '/css/template.css',
[],
['version' => 'auto']
);
// Add template JavaScript
$wa->registerAndUseScript(
'template.yourtemplate',
'templates/' . $this->template . '/js/template.js',
[],
['version' => 'auto', 'defer' => true]
);
// Add viewport meta tag for responsive design
$this->setMetaData('viewport', 'width=device-width, initial-scale=1.0');
?>
<!DOCTYPE html>
<html lang="<?php echo $this->language; ?>" dir="<?php echo $this->direction; ?>">
<head>
<jdoc:include type="metas" />
<jdoc:include type="styles" />
<jdoc:include type="scripts" />
</head>
<body class="site <?php echo $option
. ' view-' . $view
. ($layout ? ' layout-' . $layout : ' no-layout')
. ($task ? ' task-' . $task : ' no-task')
. ($itemid ? ' itemid-' . $itemid : '')
. ($pageclass ? ' ' . $pageclass : '')
. ($this->direction === 'rtl' ? ' rtl' : '');
?>">
<?php if ($this->countModules('top')) : ?>
<div class="top-bar">
<div class="<?php echo $containerFluid ? 'container-fluid' : 'container'; ?>">
<jdoc:include type="modules" name="top" style="html5" />
</div>
</div>
<?php endif; ?>
<header class="site-header<?php echo $stickyHeader ? ' sticky-header' : ''; ?>">
<div class="<?php echo $containerFluid ? 'container-fluid' : 'container'; ?>">
<div class="header-inner">
<div class="site-branding">
<?php if ($logo) : ?>
<a href="<?php echo Uri::root(); ?>" title="<?php echo $siteTitle; ?>">
<img src="<?php echo Uri::root() . htmlspecialchars($logo, ENT_QUOTES, 'UTF-8'); ?>" alt="<?php echo $siteTitle; ?>" class="site-logo">
</a>
<?php else : ?>
<div class="site-title">
<a href="<?php echo Uri::root(); ?>" title="<?php echo $siteTitle; ?>">
<?php echo $siteTitle; ?>
</a>
</div>
<?php endif; ?>
<?php if ($siteDescription) : ?>
<div class="site-description">
<?php echo htmlspecialchars($siteDescription, ENT_QUOTES, 'UTF-8'); ?>
</div>
<?php endif; ?>
</div>
<?php if ($this->countModules('header')) : ?>
<div class="header-modules">
<jdoc:include type="modules" name="header" style="html5" />
</div>
<?php endif; ?>
</div>
</div>
</header>
<?php if ($this->countModules('navigation')) : ?>
<nav class="site-navigation" role="navigation">
<div class="<?php echo $containerFluid ? 'container-fluid' : 'container'; ?>">
<jdoc:include type="modules" name="navigation" style="html5" />
</div>
</nav>
<?php endif; ?>
<?php if ($this->countModules('banner')) : ?>
<div class="banner-area">
<div class="<?php echo $containerFluid ? 'container-fluid' : 'container'; ?>">
<jdoc:include type="modules" name="banner" style="html5" />
</div>
</div>
<?php endif; ?>
<main class="site-main">
<div class="<?php echo $containerFluid ? 'container-fluid' : 'container'; ?>">
<div class="row">
<?php if ($this->countModules('sidebar-left')) : ?>
<aside class="sidebar sidebar-left col-md-3">
<jdoc:include type="modules" name="sidebar-left" style="html5" />
</aside>
<?php endif; ?>
<div class="content-area <?php
echo $this->countModules('sidebar-left') && $this->countModules('sidebar-right') ? 'col-md-6'
: ($this->countModules('sidebar-left') || $this->countModules('sidebar-right') ? 'col-md-9'
: 'col-md-12');
?>">
<?php if ($this->countModules('main-top')) : ?>
<div class="main-top">
<jdoc:include type="modules" name="main-top" style="html5" />
</div>
<?php endif; ?>
<div class="main-content">
<jdoc:include type="message" />
<jdoc:include type="component" />
</div>
<?php if ($this->countModules('main-bottom')) : ?>
<div class="main-bottom">
<jdoc:include type="modules" name="main-bottom" style="html5" />
</div>
<?php endif; ?>
</div>
<?php if ($this->countModules('sidebar-right')) : ?>
<aside class="sidebar sidebar-right col-md-3">
<jdoc:include type="modules" name="sidebar-right" style="html5" />
</aside>
<?php endif; ?>
</div>
</div>
</main>
<?php if ($this->countModules('footer')) : ?>
<footer class="site-footer">
<div class="<?php echo $containerFluid ? 'container-fluid' : 'container'; ?>">
<jdoc:include type="modules" name="footer" style="html5" />
</div>
</footer>
<?php endif; ?>
<?php if ($this->countModules('debug')) : ?>
<div class="debug-area">
<jdoc:include type="modules" name="debug" style="none" />
</div>
<?php endif; ?>
</body>
</html>
-13
View File
@@ -1,13 +0,0 @@
/**
* Custom JavaScript for yourtemplate
*
* This file is for user customizations.
* It will not be overwritten during template updates.
*
* @package Joomla.Site
* @subpackage Templates.yourtemplate
* @copyright Copyright (C) 2026. All rights reserved.
* @license GNU General Public License version 2 or later
*/
// Add your custom JavaScript code below this line
-114
View File
@@ -1,114 +0,0 @@
/**
* Template JavaScript for yourtemplate
*
* @package Joomla.Site
* @subpackage Templates.yourtemplate
* @copyright Copyright (C) 2026. All rights reserved.
* @license GNU General Public License version 2 or later
*/
(function () {
'use strict';
/**
* Initialize template functionality when DOM is ready
*/
function initTemplate() {
// Mobile menu toggle
initMobileMenu();
// Smooth scroll for anchor links
initSmoothScroll();
// Sticky header enhancement
initStickyHeader();
}
/**
* Mobile menu functionality
*
* Note: This requires a mobile menu toggle button to be added to your template.
* Add the following button in your header area:
*
* <button class="mobile-menu-toggle" aria-expanded="false" aria-label="Toggle navigation">
* <span class="menu-icon"></span>
* </button>
*
* And add CSS for the toggle button and mobile menu states.
*/
function initMobileMenu() {
const mobileMenuToggle = document.querySelector('.mobile-menu-toggle');
const navigation = document.querySelector('.site-navigation');
if (mobileMenuToggle && navigation) {
mobileMenuToggle.addEventListener('click', function () {
navigation.classList.toggle('mobile-menu-active');
const isExpanded = navigation.classList.contains('mobile-menu-active');
this.setAttribute('aria-expanded', isExpanded ? 'true' : 'false');
});
}
}
/**
* Smooth scroll for anchor links
*/
function initSmoothScroll() {
const anchorLinks = document.querySelectorAll('a[href^="#"]');
anchorLinks.forEach(function (link) {
link.addEventListener('click', function (e) {
const targetId = this.getAttribute('href');
// Skip if it's just "#" or "#top"
if (targetId === '#' || targetId === '#top') {
return;
}
const targetElement = document.querySelector(targetId);
if (targetElement) {
e.preventDefault();
targetElement.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
}
/**
* Sticky header enhancement
*/
function initStickyHeader() {
const header = document.querySelector('.site-header.sticky-header');
if (header) {
let lastScrollTop = 0;
window.addEventListener('scroll', function () {
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
// Add/remove class based on scroll direction
if (scrollTop > lastScrollTop && scrollTop > 100) {
// Scrolling down
header.classList.add('header-hidden');
} else {
// Scrolling up
header.classList.remove('header-hidden');
}
lastScrollTop = scrollTop <= 0 ? 0 : scrollTop;
});
}
}
/**
* Initialize when DOM is ready
*/
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initTemplate);
} else {
initTemplate();
}
})();
-53
View File
@@ -1,53 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage Templates.yourtemplate
*
* @copyright Copyright (C) 2026. All rights reserved.
* @license GNU General Public License version 2 or later
*/
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Uri\Uri;
/** @var Joomla\CMS\Document\HtmlDocument $this */
$app = Factory::getApplication();
$wa = $this->getWebAssetManager();
// Template params
$logo = $this->params->get('logo');
$sitename = htmlspecialchars($app->get('sitename'), ENT_QUOTES, 'UTF-8');
// Add template CSS
$wa->registerAndUseStyle(
'template.yourtemplate',
'templates/' . $this->template . '/css/template.css',
[],
['version' => 'auto']
);
?>
<!DOCTYPE html>
<html lang="<?php echo $this->language; ?>" dir="<?php echo $this->direction; ?>">
<head>
<jdoc:include type="metas" />
<jdoc:include type="styles" />
<jdoc:include type="scripts" />
</head>
<body class="site-offline">
<div class="offline-container">
<div class="offline-content">
<?php if ($logo) : ?>
<img src="<?php echo Uri::root() . htmlspecialchars($logo, ENT_QUOTES, 'UTF-8'); ?>" alt="<?php echo $sitename; ?>" class="offline-logo">
<?php else : ?>
<h1><?php echo $sitename; ?></h1>
<?php endif; ?>
<jdoc:include type="message" />
<jdoc:include type="component" />
</div>
</div>
</body>
</html>
-104
View File
@@ -1,104 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<extension type="template" client="site" method="upgrade">
<name>yourtemplate</name>
<creationDate>2026-04-27</creationDate>
<author>Your Name</author>
<authorEmail>your.email@example.com</authorEmail>
<authorUrl>https://www.example.com</authorUrl>
<copyright>Copyright (C) 2026. All rights reserved.</copyright>
<license>GNU General Public License version 2 or later</license>
<version>1.0.0</version>
<description>TPL_YOURTEMPLATE_XML_DESCRIPTION</description>
<files>
<filename>index.php</filename>
<filename>templateDetails.xml</filename>
<!--
IMPORTANT: Add actual PNG images before distribution
1. Replace template_thumbnail.png.txt and template_preview.png.txt with real PNG images
2. Uncomment the two lines below after adding the actual image files
3. See src/IMAGES.md for detailed instructions
Once images are added, uncomment these lines:
-->
<!-- <filename>template_thumbnail.png</filename> -->
<!-- <filename>template_preview.png</filename> -->
<filename>offline.php</filename>
<filename>error.php</filename>
<folder>css</folder>
<folder>js</folder>
<folder>images</folder>
<folder>fonts</folder>
<folder>language</folder>
<folder>html</folder>
</files>
<positions>
<position>top</position>
<position>header</position>
<position>navigation</position>
<position>banner</position>
<position>sidebar-left</position>
<position>sidebar-right</position>
<position>main-top</position>
<position>main</position>
<position>main-bottom</position>
<position>footer</position>
<position>debug</position>
</positions>
<config>
<fields name="params">
<fieldset name="advanced">
<field
name="logo"
type="media"
label="TPL_YOURTEMPLATE_LOGO_LABEL"
description="TPL_YOURTEMPLATE_LOGO_DESC"
/>
<field
name="siteTitle"
type="text"
label="TPL_YOURTEMPLATE_SITE_TITLE_LABEL"
description="TPL_YOURTEMPLATE_SITE_TITLE_DESC"
default=""
/>
<field
name="siteDescription"
type="textarea"
label="TPL_YOURTEMPLATE_SITE_DESCRIPTION_LABEL"
description="TPL_YOURTEMPLATE_SITE_DESCRIPTION_DESC"
rows="3"
cols="30"
/>
<field
name="containerFluid"
type="radio"
label="TPL_YOURTEMPLATE_CONTAINER_FLUID_LABEL"
description="TPL_YOURTEMPLATE_CONTAINER_FLUID_DESC"
default="0"
layout="joomla.form.field.radio.switcher"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="stickyHeader"
type="radio"
label="TPL_YOURTEMPLATE_STICKY_HEADER_LABEL"
description="TPL_YOURTEMPLATE_STICKY_HEADER_DESC"
default="0"
layout="joomla.form.field.radio.switcher"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
</fieldset>
</fields>
</config>
</extension>