Compare commits

...

178 Commits
rc ... dev

Author SHA1 Message Date
Jonathan Miller b491241a58 Merge branch 'main' into dev
Universal: Sync Feature Branch Versions / Sync feature branches with dev (push) Has been skipped
# Conflicts:
#	.mokogitea/CLAUDE.md
#	.mokogitea/ISSUE_TEMPLATE/config.yml
#	.mokogitea/ISSUE_TEMPLATE/documentation.md
#	.mokogitea/ISSUE_TEMPLATE/feature_request.md
#	.mokogitea/ISSUE_TEMPLATE/security.md
#	.mokogitea/branch-protection.yml
#	.mokogitea/bulk-repo-sync.yml
#	.mokogitea/pr-branch-check.yml
#	.mokogitea/renovate.yml
#	.mokogitea/sync-wikis.yml
#	.mokogitea/workflows/auto-bump.yml
#	.mokogitea/workflows/auto-release.yml
#	.mokogitea/workflows/ci-platform.yml
#	.mokogitea/workflows/cleanup.yml
#	.mokogitea/workflows/gitleaks.yml
#	.mokogitea/workflows/issue-branch.yml
#	.mokogitea/workflows/notify.yml
#	.mokogitea/workflows/pre-release.yml
#	.mokogitea/workflows/repo-health.yml
#	.mokogitea/workflows/security-audit.yml
#	.script-registry.json
#	CHANGELOG.md
#	PLUGIN_SCRIPTS.md
#	README.md
#	analysis/index.md
#	automation/bulk_joomla_template.php
#	automation/bulk_sync.php
#	automation/enrich_manifest_xml.php
#	automation/enrich_mokostandards_xml.php
#	automation/index.md
#	automation/migrate_to_gitea.php
#	automation/push_files.php
#	automation/push_manifest_xml.php
#	automation/push_mokostandards_xml.php
#	automation/repo_cleanup.php
#	bin/moko
#	cli/archive_repo.php
#	cli/audit_query.php
#	cli/badge_update.php
#	cli/branch_rename.php
#	cli/bulk_workflow_push.php
#	cli/bulk_workflow_trigger.php
#	cli/changelog_promote.php
#	cli/changelog_prune.php
#	cli/client_dashboard.php
#	cli/client_health_check.php
#	cli/client_inventory.php
#	cli/client_provision.php
#	cli/completion.php
#	cli/create_project.php
#	cli/create_repo.php
#	cli/deploy_joomla.php
#	cli/dev_branch_reset.php
#	cli/grafana_dashboard.php
#	cli/joomla_build.php
#	cli/joomla_compat_check.php
#	cli/joomla_metadata_validate.php
#	cli/joomla_release.php
#	cli/license_manage.php
#	cli/manifest_element.php
#	cli/manifest_licensing.php
#	cli/manifest_read.php
#	cli/package_build.php
#	cli/platform_detect.php
#	cli/release.php
#	cli/release_body_update.php
#	cli/release_cascade.php
#	cli/release_create.php
#	cli/release_manage.php
#	cli/release_mirror.php
#	cli/release_notes.php
#	cli/release_package.php
#	cli/release_promote.php
#	cli/release_publish.php
#	cli/release_validate.php
#	cli/release_verify.php
#	cli/scaffold_client.php
#	cli/sync_rulesets.php
#	cli/theme_lint.php
#	cli/updates_xml_build.php
#	cli/updates_xml_sync.php
#	cli/version_auto_bump.php
#	cli/version_bump.php
#	cli/version_bump_remote.php
#	cli/version_check.php
#	cli/version_read.php
#	cli/version_reset_dev.php
#	cli/version_set_platform.php
#	cli/wiki_sync.php
#	cli/workflow_sync.php
#	composer.json
#	deploy/backup-before-deploy.php
#	deploy/deploy-dolibarr.php
#	deploy/deploy-joomla.php
#	deploy/deploy-sftp.php
#	deploy/health-check.php
#	deploy/rollback-joomla.php
#	deploy/sync-joomla.php
#	fix/fix_line_endings.php
#	fix/fix_permissions.php
#	fix/fix_tabs.php
#	fix/fix_trailing_spaces.php
#	fix/index.md
#	index.md
#	lib/CliBase.php
#	lib/Common.php
#	lib/Enterprise/AbstractProjectPlugin.php
#	lib/Enterprise/ApiClient.php
#	lib/Enterprise/AuditLogger.php
#	lib/Enterprise/CheckpointManager.php
#	lib/Enterprise/CliFramework.php
#	lib/Enterprise/Config.php
#	lib/Enterprise/ConfigValidator.php
#	lib/Enterprise/EnterpriseReadinessValidator.php
#	lib/Enterprise/ErrorRecovery.php
#	lib/Enterprise/FileFixUtility.php
#	lib/Enterprise/GitHubAdapter.php
#	lib/Enterprise/GitPlatformAdapter.php
#	lib/Enterprise/InputValidator.php
#	lib/Enterprise/ManifestParser.php
#	lib/Enterprise/ManifestReader.php
#	lib/Enterprise/MetricsCollector.php
#	lib/Enterprise/MokoGiteaAdapter.php
#	lib/Enterprise/PackageBuilder.php
#	lib/Enterprise/PlatformAdapterFactory.php
#	lib/Enterprise/PluginFactory.php
#	lib/Enterprise/PluginRegistry.php
#	lib/Enterprise/Plugins/ApiPlugin.php
#	lib/Enterprise/Plugins/DocumentationPlugin.php
#	lib/Enterprise/Plugins/DolibarrPlugin.php
#	lib/Enterprise/Plugins/GenericPlugin.php
#	lib/Enterprise/Plugins/JoomlaPlugin.php
#	lib/Enterprise/Plugins/McpServerPlugin.php
#	lib/Enterprise/Plugins/MobilePlugin.php
#	lib/Enterprise/Plugins/NodeJsPlugin.php
#	lib/Enterprise/Plugins/PythonPlugin.php
#	lib/Enterprise/Plugins/TerraformPlugin.php
#	lib/Enterprise/Plugins/WordPressPlugin.php
#	lib/Enterprise/ProjectConfigValidator.php
#	lib/Enterprise/ProjectMetricsCollector.php
#	lib/Enterprise/ProjectPluginInterface.php
#	lib/Enterprise/ProjectTypeDetector.php
#	lib/Enterprise/RecoveryError.php
#	lib/Enterprise/RecoveryManager.php
#	lib/Enterprise/RepositoryHealthChecker.php
#	lib/Enterprise/RepositorySynchronizer.php
#	lib/Enterprise/RetryHelper.php
#	lib/Enterprise/SecurityValidator.php
#	lib/Enterprise/SourceResolver.php
#	lib/Enterprise/SynchronizationException.php
#	lib/Enterprise/TransactionManager.php
#	lib/Enterprise/UnifiedValidation.php
#	lib/index.md
#	lib/plugins/Joomla/UpdateXmlGenerator.php
#	maintenance/index.md
#	maintenance/pin_action_shas.php
#	maintenance/repo_inventory.php
#	maintenance/rotate_secrets.php
#	maintenance/setup_labels.php
#	maintenance/sync_dolibarr_readmes.php
#	maintenance/update_repo_inventory.php
#	maintenance/update_sha_hashes.php
#	maintenance/update_version_from_readme.php
#	mcp/config.example.json
#	mcp/package.json
#	mcp/src/config.ts
#	mcp/src/index.ts
#	mcp/src/runner.ts
#	mcp/src/types.ts
#	phpcs.xml
#	plugin_health_check.php
#	plugin_list.php
#	plugin_metrics.php
#	plugin_readiness.php
#	plugin_validate.php
#	release/generate_dolibarr_version_txt.php
#	release/generate_joomla_update_xml.php
#	src/functions.php
#	templates/configs/README.md
#	templates/configs/index.md
#	templates/configs/manifest.xml.template
#	templates/configs/manifest.yml.template
#	templates/configs/mokostandards.xml.template
#	templates/configs/mokostandards.yml.template
#	templates/configs/phpcs.xml
#	templates/docs/README.md
#	templates/docs/extra/README.md
#	templates/docs/extra/index.md
#	templates/docs/index.md
#	templates/docs/required/GOVERNANCE.md
#	templates/docs/required/README.md
#	templates/docs/required/index.md
#	templates/docs/required/template-CONTRIBUTING.md
#	templates/docs/required/template-README.md
#	templates/docs/required/template-SECURITY.md
#	templates/index.md
#	templates/licenses/README.md
#	templates/licenses/index.md
#	templates/makefiles/README.md
#	templates/mokogitea/CLAUDE.dolibarr.md.template
#	templates/mokogitea/CLAUDE.joomla.md.template
#	templates/mokogitea/CLAUDE.md.template
#	templates/mokogitea/ISSUE_TEMPLATE/config.yml
#	templates/mokogitea/ISSUE_TEMPLATE/documentation.md
#	templates/mokogitea/ISSUE_TEMPLATE/dolibarr_module_id_request.md
#	templates/mokogitea/ISSUE_TEMPLATE/feature_request.md
#	templates/mokogitea/ISSUE_TEMPLATE/security.md
#	templates/mokogitea/README.md
#	templates/mokogitea/copilot-instructions.dolibarr.md.template
#	templates/mokogitea/copilot-instructions.joomla.md.template
#	templates/mokogitea/copilot-instructions.md.template
#	templates/mokogitea/dependabot.yml.template
#	templates/mokogitea/override.tf.template
#	templates/required/README.md
#	templates/schemas/README.md
#	templates/schemas/manifest-schema.xsd
#	templates/schemas/moko-platform-schema.xsd
#	templates/schemas/mokostandards-schema.xsd
#	templates/schemas/schemas/README.md
#	templates/schemas/template-repository-structure.xml
#	templates/scripts/README.md
#	templates/scripts/common/CliBase.template.php
#	templates/scripts/fix/index.md
#	templates/scripts/index.md
#	templates/scripts/release/index.md
#	templates/scripts/release/package_dolibarr.php
#	templates/scripts/release/package_joomla.php
#	templates/scripts/sftp-config/README.md
#	templates/scripts/validate/dolibarr_module.php
#	templates/scripts/validate/index.md
#	templates/scripts/validate/validate_manifest.php
#	templates/scripts/validate/validate_structure.php
#	templates/security/README.md
#	templates/security/index.php
#	templates/stubs/dolibarr.php
#	templates/stubs/joomla.php
#	templates/web/index.php
#	tests/Enterprise/GitPlatformAdapterTest.php
#	tests/Unit/VersionBumpTest.php
#	tests/Unit/VersionReadTest.php
#	tests/index.md
#	tests/test_circuit_breaker_handling.php
#	tests/test_enterprise_libraries.php
#	validate/SECURITY_SCANNING.md
#	validate/auto_detect_platform.php
#	validate/check_changelog.php
#	validate/check_client_theme.php
#	validate/check_composer_deps.php
#	validate/check_dolibarr_module.php
#	validate/check_enterprise_readiness.php
#	validate/check_file_integrity.php
#	validate/check_joomla_manifest.php
#	validate/check_language_structure.php
#	validate/check_license_headers.php
#	validate/check_no_secrets.php
#	validate/check_paths.php
#	validate/check_php_syntax.php
#	validate/check_repo_health.php
#	validate/check_structure.php
#	validate/check_tabs.php
#	validate/check_version_consistency.php
#	validate/check_wiki_health.php
#	validate/check_xml_wellformed.php
#	validate/index.md
#	validate/scan_drift.php
#	wrappers/auto_detect_platform.php
#	wrappers/bulk_sync.php
#	wrappers/check_changelog.php
#	wrappers/check_dolibarr_module.php
#	wrappers/check_enterprise_readiness.php
#	wrappers/check_joomla_manifest.php
#	wrappers/check_language_structure.php
#	wrappers/check_license_headers.php
#	wrappers/check_no_secrets.php
#	wrappers/check_paths.php
#	wrappers/check_php_syntax.php
#	wrappers/check_repo_health.php
#	wrappers/check_structure.php
#	wrappers/check_tabs.php
#	wrappers/check_version_consistency.php
#	wrappers/check_xml_wellformed.php
#	wrappers/deploy_sftp.php
#	wrappers/fix_line_endings.php
#	wrappers/fix_permissions.php
#	wrappers/fix_tabs.php
#	wrappers/fix_trailing_spaces.php
#	wrappers/gen_wrappers.php
#	wrappers/index.md
#	wrappers/pin_action_shas.php
#	wrappers/plugin_health_check.php
#	wrappers/plugin_list.php
#	wrappers/plugin_metrics.php
#	wrappers/plugin_readiness.php
#	wrappers/plugin_validate.php
#	wrappers/scan_drift.php
#	wrappers/setup_labels.php
#	wrappers/sync_dolibarr_readmes.php
#	wrappers/update_sha_hashes.php
#	wrappers/update_version_from_readme.php
2026-06-20 21:43:38 -05:00
Jonathan Miller d5fa609f15 fix(version-bump): prevent dev from falling behind stable (#283)
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 10s
version_bump.php now checks two additional sources before bumping:

1. --min-version argument: workflow can pass the current stable version
   so the bump is guaranteed to be above it
2. Auto-detect from git: scans commit messages and remote branch
   manifests (origin/main, origin/rc) to find the highest version
   released on any channel. If a higher version exists on another
   branch, uses that as the base instead of the local manifest.

This prevents the scenario where dev builds at 02.44.xx while stable
is at 02.45.00 — the bump will now use 02.45.00 as the base and
produce 02.45.01-dev or 02.46.00-dev depending on bump type.
2026-06-20 21:35:47 -05:00
gitea-actions[bot] 1655e2a0ae chore: promote changelog [Unreleased] → [09.32.00] 2026-06-21 02:04:14 +00:00
gitea-actions[bot] 4aef631244 chore(release): build 09.32.00 [skip ci]
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 1: Code Quality (push) Failing after 1m3s
2026-06-21 02:04:11 +00:00
jmiller 6b2cf099f7 Merge pull request 'feat: SourceResolver queries Gitea Manifest API for entry_point (#249)' (#286) from feature/249-source-resolver-api into main
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 1: Code Quality (push) Failing after 1m1s
2026-06-21 02:04:01 +00:00
gitea-actions[bot] fefa44965f chore(version): auto-bump patch 09.31.01-dev [skip ci]
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 3s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 14s
2026-06-21 02:02:20 +00:00
Jonathan Miller 2b7e38b711 feat: SourceResolver queries Gitea Manifest API for entry_point (#249)
Universal: Auto Version Bump / Version Bump (push) Successful in 12s
SourceResolver::resolve() now checks the Gitea Manifest API first when
GA_TOKEN/GITEA_TOKEN and GITHUB_REPOSITORY are available (CI environments).
Falls back to filesystem detection (source/, src/, htdocs/) when offline.

- API results cached per org/repo for process lifetime
- 5s timeout to avoid blocking local dev
- resolveFromApi() also available as standalone method
- org/repo derived from GITHUB_REPOSITORY env or git remote URL
2026-06-20 21:02:04 -05:00
jmiller 3ed575906f chore: sync issue-branch.yml from Template-Generic [skip ci] 2026-06-21 01:40:36 +00:00
gitea-actions[bot] c775cb9447 chore: promote changelog [Unreleased] → [09.31.00] 2026-06-21 01:40:16 +00:00
gitea-actions[bot] 89542f6018 chore(release): build 09.31.00 [skip ci]
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 1: Code Quality (push) Failing after 1m25s
2026-06-21 01:40:13 +00:00
jmiller bd97b6f79c Merge pull request 'feat: sync feature branch versions with dev after bumps (#250)' (#285) from feature/250-sync-feature-branch-versions into main
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 1: Code Quality (push) Failing after 1m28s
2026-06-21 01:39:59 +00:00
Jonathan Miller c461cc5520 feat: sync feature branch versions with dev after bumps (#250)
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Universal: PR Check / Branch Policy (pull_request) Failing after 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Universal: PR Check / Secret Scan (pull_request) Successful in 6s
Universal: Auto Version Bump / Version Bump (push) Successful in 11s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 18s
Platform: mokoplatform CI / Gate 1: Code Quality (pull_request) Failing after 1m20s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 2m10s
Add workflow that merges dev into all open feature/fix/patch/hotfix/bugfix/chore
branches when dev gets a version bump commit. Branches with merge conflicts are
skipped with a warning. Can also be triggered manually via workflow_dispatch.
2026-06-20 20:39:38 -05:00
jmiller ae4191bad0 chore: sync workflow-sync-trigger.yml from Template-Generic [skip ci] 2026-06-21 01:27:28 +00:00
jmiller 73f3d0e734 chore: sync repo-health.yml from Template-Generic [skip ci] 2026-06-21 01:27:27 +00:00
jmiller 0e7ca2f19a chore: sync rc-revert.yml from Template-Generic [skip ci] 2026-06-21 01:27:26 +00:00
jmiller 685089f60c chore: sync pr-check.yml from Template-Generic [skip ci] 2026-06-21 01:27:24 +00:00
jmiller 4d3aef3cc5 chore: sync issue-branch.yml from Template-Generic [skip ci] 2026-06-21 01:27:23 +00:00
jmiller d6d05a1075 chore: sync deploy-manual.yml from Template-Generic [skip ci] 2026-06-21 01:27:22 +00:00
jmiller 1c8bf4e76e chore: sync ci-generic.yml from Template-Generic [skip ci] 2026-06-21 01:27:21 +00:00
jmiller 6e99d0fac6 chore: sync auto-release.yml from Template-Generic [skip ci] 2026-06-21 01:27:19 +00:00
gitea-actions[bot] 5161072b0e chore: promote changelog [Unreleased] → [09.30.00]
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
2026-06-21 01:26:33 +00:00
gitea-actions[bot] 7a4358454d chore(release): build 09.30.00 [skip ci]
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (push) Blocked by required conditions
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Platform: mokoplatform CI / Gate 1: Code Quality (push) Failing after 1m29s
2026-06-21 01:26:29 +00:00
jmiller a0cc0953c7 Merge pull request 'feat: security advisory aggregator, manifest API rewrite, namespace rename' (#284) from feature/150-security-advisory-aggregator into main
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (push) Blocked by required conditions
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Platform: mokoplatform CI / Gate 1: Code Quality (push) Failing after 1m33s
2026-06-21 01:26:10 +00:00
gitea-actions[bot] 79c853354b chore(version): auto-bump patch 09.29.02-dev [skip ci]
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 21s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 4m3s
2026-06-21 01:25:35 +00:00
Jonathan Miller 033e948c79 feat: security advisory aggregator, manifest API rewrite, namespace rename (#150, #283)
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Universal: Security Audit / Dependency Audit (pull_request) Successful in 7s
Universal: Auto Version Bump / Version Bump (push) Successful in 14s
Platform: mokoplatform CI / Gate 1: Code Quality (pull_request) Failing after 1m8s
- Add `security:advisories` command — cross-repo CVE scanner via composer audit
  with checkpoint resumability, severity filtering, and auto-issue creation
- Rewrite `manifest:read` to use Gitea manifest API as primary source with
  auto-detection fallback from source tree (no more manifest.xml dependency)
- Rename MokoStandards namespace → MokoCli across all files
- Rename MokoEnterprise namespace → MokoCli across all files
- Rename MokoStandardsParser class → ManifestParser
- Fix composer.json autoload paths: src/ → source/
2026-06-20 20:24:58 -05:00
jmiller 016fda394b chore: delete manifest.xml (no longer used)
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 1: Code Quality (push) Failing after 1m10s
2026-06-21 00:19:36 +00:00
jmiller 8b18bd73e5 chore: sync pr-check.yml from Template-Generic [skip ci] 2026-06-20 23:45:51 +00:00
jmiller ffba748762 chore: sync gitleaks.yml from Template-Generic [skip ci] 2026-06-20 23:45:50 +00:00
jmiller d67ff279a1 chore: sync ci-generic.yml from Template-Generic [skip ci] 2026-06-20 23:45:50 +00:00
jmiller fbbc170333 chore: sync workflow-sync-trigger.yml from Template-Generic [skip ci] 2026-06-20 20:50:56 +00:00
jmiller b574799ca1 chore: sync security-audit.yml from Template-Generic [skip ci] 2026-06-20 20:50:55 +00:00
jmiller 79866a15e5 chore: sync repo-health.yml from Template-Generic [skip ci] 2026-06-20 20:50:54 +00:00
jmiller a3c18a0248 chore: sync pre-release.yml from Template-Generic [skip ci] 2026-06-20 20:50:53 +00:00
jmiller 57392c254e chore: sync pr-check.yml from Template-Generic [skip ci] 2026-06-20 20:50:52 +00:00
jmiller acdc0e903c chore: sync notify.yml from Template-Generic [skip ci] 2026-06-20 20:50:51 +00:00
jmiller 55af5b0feb chore: sync issue-branch.yml from Template-Generic [skip ci] 2026-06-20 20:50:50 +00:00
jmiller 47ab0de146 chore: sync gitleaks.yml from Template-Generic [skip ci] 2026-06-20 20:50:49 +00:00
jmiller cfdb0094ba chore: sync deploy-manual.yml from Template-Generic [skip ci] 2026-06-20 20:50:48 +00:00
jmiller 5e8092a1c9 chore: sync cleanup.yml from Template-Generic [skip ci] 2026-06-20 20:50:47 +00:00
jmiller 3699edbf62 chore: sync ci-generic.yml from Template-Generic [skip ci] 2026-06-20 20:50:46 +00:00
jmiller c5860d4074 chore: sync branch-cleanup.yml from Template-Generic [skip ci] 2026-06-20 20:50:45 +00:00
jmiller 5d8fa2b1d3 chore: sync auto-release.yml from Template-Generic [skip ci] 2026-06-20 20:50:44 +00:00
jmiller 9f3dd6f790 chore: sync auto-bump.yml from Template-Generic [skip ci] 2026-06-20 20:50:43 +00:00
jmiller 4ccc850edf Merge pull request 'fix: add missing src/functions.php referenced by composer autoload' (#282) from fix/missing-functions-php into main
Generic: Project CI / Tests (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (push) Blocked by required conditions
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Generic: Project CI / Lint & Validate (push) Failing after 38s
Platform: mokoplatform CI / Gate 1: Code Quality (push) Failing after 1m19s
2026-06-20 19:03:55 +00:00
Moko Consulting 957460db03 fix: add missing src/functions.php referenced by composer autoload
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Branch Cleanup / Delete merged branch (pull_request) Failing after 2s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 7s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || 'development' }}) (pull_request_target) Failing after 11s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Generic: Project CI / Lint & Validate (pull_request) Failing after 23s
Platform: mokoplatform CI / Gate 1: Code Quality (pull_request) Failing after 1m13s
2026-06-20 18:48:40 +00:00
jmiller 8b014ad0a9 feat: implement release zip cascade to lower stability channels (#280)
Generic: Project CI / Tests (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (push) Blocked by required conditions
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Access control (push) Successful in 2s
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Project CI / Lint & Validate (push) Failing after 57s
Platform: mokoplatform CI / Gate 1: Code Quality (push) Failing after 1m16s
2026-06-20 17:40:03 +00:00
jmiller f942615a12 fix: add branch+PR fallback for protected repos, rename moko-platform to mokocli
Generic: Project CI / Tests (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (push) Blocked by required conditions
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Project CI / Lint & Validate (push) Failing after 39s
Platform: mokoplatform CI / Gate 1: Code Quality (push) Failing after 44s
2026-06-20 17:30:34 +00:00
jmiller 116d94dd8c fix: add branch+PR fallback for protected repos, rename moko-platform to mokocli
Generic: Project CI / Tests (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (push) Blocked by required conditions
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Access control (push) Successful in 2s
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Project CI / Lint & Validate (push) Failing after 44s
Platform: mokoplatform CI / Gate 1: Code Quality (push) Failing after 47s
2026-06-20 17:30:33 +00:00
jmiller a918cb38b2 Merge pull request 'fix: rename package_type to extension_type, remove display_name (#259)' (#281) from fix/259-cherry-pick into main
Generic: Project CI / Tests (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (push) Blocked by required conditions
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Project CI / Lint & Validate (push) Failing after 32s
Platform: mokoplatform CI / Gate 1: Code Quality (push) Failing after 46s
2026-06-20 17:15:50 +00:00
Jonathan Miller dc7f6c9eeb fix: improve error handling, add extension_type fallback (#259)
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || 'development' }}) (pull_request_target) Failing after 14s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Failing after 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 10s
Generic: Project CI / Lint & Validate (pull_request) Failing after 44s
Platform: mokoplatform CI / Gate 1: Code Quality (pull_request) Failing after 1m17s
Branch Cleanup / Delete merged branch (pull_request) Failing after 2s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
- Surface HTTP errors instead of suppressing with @file_get_contents
- Add specific messages for 401/403/404 and missing token
- Fall back to package_type if extension_type not in API response
- Log warnings for malformed XML candidates
- Fix platform_detect.php endpoint from /manifest to /metadata
2026-06-20 12:14:08 -05:00
Jonathan Miller 0b1f39a75c fix: rename package_type to extension_type, remove display_name validation (#259)
- API endpoint updated from /manifest to /metadata
- Removed dead .mokogitea/manifest.xml local file fallback
- display_name is now server-computed, no longer validated
- package_type renamed to extension_type throughout
2026-06-20 12:14:07 -05:00
jmiller 8822469761 fix: rename mokoplatform/moko-platform to MokoCLI everywhere (#262)
Platform: MokoCLI CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: MokoCLI CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: MokoCLI CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: MokoCLI CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: MokoCLI CI / Gate 4: Governance (push) Blocked by required conditions
Platform: MokoCLI CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: MokoCLI CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: MokoCLI CI / CI Summary (push) Blocked by required conditions
Platform: MokoCLI CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: MokoCLI CI / CI Summary (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Site Health (push) Has been skipped
Universal: Auto Version Bump / Version Bump (push) Failing after 4s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 5s
Universal: PR Check / Validate PR (pull_request) Successful in 5s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 6s
Universal: Security Audit / Dependency Audit (pull_request) Successful in 5s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || 'development' }}) (pull_request_target) Failing after 9s
Platform: MokoCLI CI / Gate 1: Code Quality (push) Failing after 59s
Platform: MokoCLI CI / Gate 1: Code Quality (pull_request) Failing after 1m1s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
2026-06-20 17:12:15 +00:00
Jonathan Miller 908e839c30 fix: address PR review findings for #262 rename
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Platform: MokoCLI CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: MokoCLI CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 4s
Universal: PR Check / Validate PR (pull_request) Successful in 6s
Platform: MokoCLI CI / Gate 1: Code Quality (pull_request) Failing after 50s
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
Critical fixes:
- Standardize all workflow paths to /opt/mokocli (lowercase), not /opt/MokoCLI
- Add legacy /opt/mokoplatform fallback to auto-release.yml and pre-release.yml
- Fix inconsistent REPO header URLs to MokoConsulting/mokocli everywhere
- Fix auto-bump.yml clone URL from mokoplatform.git to mokocli.git

Alias improvements:
- Remove @ suppression from trigger_error so deprecation notices are observable
- Add dependency guard for mokostandards_* functions at top of aliases.php
- Add E_USER_NOTICE diagnostic when neither install path is found
- Improve setup-mokocli-aliases.sh with WARNING messages for edge cases

Missed files:
- Rename references in shell scripts (ci-issue-reporter, server-autoheal, sync-wikis)
- Rename references in XSD schemas and .template files
- Remove accidentally force-added bin/validate-module (gitignored)
2026-06-20 12:07:55 -05:00
jmiller d5d9c4afce fix: rename moko-platform to mokocli + changelog promotion in workflows
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Site Health (push) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Validate PR (pull_request) Successful in 5s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 5s
2026-06-20 17:03:53 +00:00
jmiller ba6003a431 fix: rename moko-platform to mokocli + changelog promotion in workflows
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Platform: MokoCLI CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: MokoCLI CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Validate PR (pull_request) Successful in 6s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 5s
Platform: MokoCLI CI / Gate 1: Code Quality (pull_request) Failing after 1m15s
2026-06-20 17:03:49 +00:00
jmiller f918bccf12 fix: rename moko-platform to mokocli + changelog promotion in workflows
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Platform: MokoCLI CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: MokoCLI CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Successful in 6s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 6s
Platform: MokoCLI CI / Gate 1: Code Quality (pull_request) Failing after 1m22s
2026-06-20 17:03:45 +00:00
jmiller 8741964d87 fix: rename moko-platform to mokocli + changelog promotion in workflows
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Platform: MokoCLI CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: MokoCLI CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Successful in 6s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 6s
Platform: MokoCLI CI / Gate 1: Code Quality (pull_request) Failing after 1m19s
2026-06-20 17:03:42 +00:00
jmiller c70481b69d fix: rename moko-platform to mokocli + changelog promotion in workflows
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Platform: MokoCLI CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: MokoCLI CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 5s
Universal: PR Check / Validate PR (pull_request) Successful in 6s
Platform: MokoCLI CI / Gate 1: Code Quality (pull_request) Failing after 1m24s
2026-06-20 17:03:39 +00:00
Jonathan Miller a5a16fb7e3 fix: rename moko-platform to MokoCLI in documentation (#268)
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Platform: MokoCLI CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: MokoCLI CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: MokoCLI CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Universal: PR Check / Branch Policy (pull_request) Successful in 4s
Universal: PR Check / Validate PR (pull_request) Successful in 8s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Platform: MokoCLI CI / Gate 1: Code Quality (pull_request) Failing after 1m31s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 5s
Update all markdown files: README, CHANGELOG, CLAUDE.md, index files,
templates, issue templates, and inline documentation.
2026-06-20 11:39:54 -05:00
Jonathan Miller 1274e2866b fix: rename moko-platform to MokoCLI in MCP server configs (#269)
Update REPO URLs and references in all MCP TypeScript source files.
2026-06-20 11:38:59 -05:00
Jonathan Miller 314bf91913 fix: add deprecation notices to backward-compat aliases (#276)
Old-name aliases now emit E_USER_DEPRECATED so usage appears in logs
without interrupting execution. Added mokoplatform_version() and
mokoplatform_root_dir() backward aliases with deprecation warnings.
The install path resolver also warns when falling back to /opt/mokoplatform.
2026-06-20 11:38:00 -05:00
Jonathan Miller 5c05ac62d0 fix: rename moko-platform to MokoCLI in CI/CD workflows (#266)
Update all .mokogitea/ workflow and config YAML files:
- File header comments (INGROUP, REPO, DEFGROUP)
- CI banner text in ci-platform.yml
- EXCLUDE lists accept both mokocli and mokoplatform repo names
- auto-bump.yml already updated in #276 with dual-path resolution
2026-06-20 11:37:08 -05:00
Jonathan Miller d206dd5afb fix: rename MokoStandards to MokoCLI in CLI help text and output (#265)
Update bin/moko banner, help text, and file headers.
Update bin/validate-module output and headers.
2026-06-20 11:24:42 -05:00
Jonathan Miller 50260b5cb5 fix: rename MokoPlatform/moko-platform to MokoCLI in all PHP files (#264)
Bulk rename across all non-vendor PHP files:
- File header comments (DEFGROUP, INGROUP, REPO, @package)
- User-Agent strings
- Namespace URI constant in MokoStandardsParser
- Descriptive comments and docblocks
2026-06-20 11:22:24 -05:00
Jonathan Miller 4eb421d6ba fix: rename moko-platform to MokoCLI in config files (#263)
Update package names, descriptions, and references across all
configuration files: composer.json, mcp/package.json, phpcs.xml,
issue templates, script registry, and schema templates.
2026-06-20 11:20:27 -05:00
Jonathan Miller df83f36436 fix: add MokoCLI backward-compatibility aliases and redirect stubs (#276)
Prepare for the mokoplatform -> MokoCLI rename by introducing aliases
so both old and new names work during the transition:

- src/aliases.php: forward function aliases (mokocli_version, etc.)
  and install path resolver accepting both /opt/mokocli and /opt/mokoplatform
- scripts/setup-mokocli-aliases.sh: runner provisioning script to create
  symlinks between mokocli and mokoplatform paths
- composer.json: auto-load aliases.php
- mcp/package.json: add mokocli-mcp bin alias alongside moko-platform-mcp
- auto-bump.yml: check both /opt/mokocli and /opt/mokoplatform paths
- platform_detect.php: accept both mokocli and mokoplatform directory names
2026-06-20 11:17:08 -05:00
jmiller db21aca7d0 Merge pull request 'fix: rename Template-Client-Waas submodule to Template-Client' (#275) from fix/rename-client-waas-submodule into main
Generic: Project CI / Tests (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (push) Blocked by required conditions
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Access control (push) Successful in 2s
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Project CI / Lint & Validate (push) Failing after 49s
Platform: mokoplatform CI / Gate 1: Code Quality (push) Failing after 58s
2026-06-20 16:00:42 +00:00
Jonathan Miller 463ae43e64 fix: rename Template-Client-Waas submodule to Template-Client
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Universal: PR Check / Branch Policy (pull_request) Failing after 1s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 9s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Generic: Project CI / Lint & Validate (pull_request) Failing after 44s
Platform: mokoplatform CI / Gate 1: Code Quality (pull_request) Failing after 1m41s
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
2026-06-20 11:00:15 -05:00
jmiller 93c524b655 Merge pull request 'refactor: rename src/ to source/' (#274) from fix/rename-src-to-source-v2 into main
Generic: Project CI / Tests (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (push) Blocked by required conditions
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Project CI / Lint & Validate (push) Failing after 30s
Platform: mokoplatform CI / Gate 1: Code Quality (push) Failing after 1m1s
2026-06-20 16:00:11 +00:00
jmiller 2e0f3554db Merge pull request 'refactor: replace template dirs with git submodules' (#273) from fix/templates-to-submodules into main
Generic: Project CI / Tests (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (push) Blocked by required conditions
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Access control (push) Successful in 2s
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Project CI / Lint & Validate (push) Failing after 1m1s
Platform: mokoplatform CI / Gate 1: Code Quality (push) Failing after 1m41s
2026-06-20 15:58:25 +00:00
Jonathan Miller 5521a7f81e refactor: replace template dirs with git submodules
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Universal: PR Check / Branch Policy (pull_request) Failing after 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 7s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Generic: Project CI / Lint & Validate (pull_request) Failing after 24s
Branch Cleanup / Delete merged branch (pull_request) Failing after 1s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Platform: mokoplatform CI / Gate 1: Code Quality (pull_request) Failing after 1m46s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Remove embedded template copies from templates/repos/ and replace
with submodules pointing to the canonical template repos:

- Template-Client-Waas -> Template-Client
- Template-Generic -> Template-Generic
- Template-Joomla -> Template-Joomla
- Template-MCP -> Template-MCP

Dolibarr template removed (no longer needed).
Submodule paths match repo names for consistency.

Use `git submodule update --init` to populate after clone.
2026-06-20 10:56:40 -05:00
Jonathan Miller 3331442381 refactor: rename src/ to source/ for consistency
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Project CI / Lint & Validate (pull_request) Failing after 31s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 9s
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 9s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
Platform: mokoplatform CI / Gate 1: Code Quality (pull_request) Failing after 1m12s
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
2026-06-20 10:56:35 -05:00
Jonathan Miller 62b19d516f refactor: replace template dirs with git submodules
Remove embedded template copies from templates/repos/ and replace
with submodules pointing to the canonical template repos:

- client-waas -> Template-Client
- generic -> Template-Generic
- joomla -> Template-Joomla
- mcp -> Template-MCP

Dolibarr template removed (no longer needed).

Templates are now maintained in their own repos. Use
`git submodule update --init` to populate after clone.
2026-06-20 10:52:08 -05:00
jmiller 8d2ac56f0c Merge pull request 'chore: rename GA_TOKEN/GH_TOKEN secret references' (#272) from fix/rename-secret-refs into main
Generic: Project CI / Tests (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (push) Blocked by required conditions
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Project CI / Lint & Validate (push) Failing after 31s
Platform: mokoplatform CI / Gate 1: Code Quality (push) Failing after 1m17s
2026-06-20 15:30:20 +00:00
Jonathan Miller 12a16b178a chore: rename secret references for readability
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Universal: PR Check / Branch Policy (pull_request) Failing after 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 9s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 10s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Failing after 1s
Generic: Project CI / Lint & Validate (pull_request) Failing after 27s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || 'development' }}) (pull_request_target) Failing after 12s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Platform: mokoplatform CI / Gate 1: Code Quality (pull_request) Failing after 1m22s
GA_TOKEN -> MOKOGITEA_TOKEN
GH_TOKEN -> GH_PAT

Applied across all workflows, templates, and embedded MCP servers.
Secrets need to be updated in repo settings to match.
2026-06-20 10:29:48 -05:00
gitea-actions[bot] e4de039cff chore(version): pre-release bump to 09.25.05-dev [skip ci] 2026-06-19 08:16:30 +00:00
jmiller a6338493aa Merge pull request 'fix: rename package_type to extension_type, remove display_name (#259)' (#261) from fix/259-metadata-rename 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 9s
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 38s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / 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-19 08:16:20 +00:00
gitea-actions[bot] 1b113af068 chore(version): pre-release bump to 09.25.04-dev [skip ci]
Branch Cleanup / Delete merged branch (pull_request) Failing after 1s
2026-06-19 08:14:01 +00:00
Jonathan Miller a51f0bfb2f fix: rename package_type to extension_type, remove display_name validation (#259)
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Site Health (push) Has been skipped
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 6s
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
- API endpoint updated from /manifest to /metadata
- Removed dead .mokogitea/manifest.xml local file fallback
- display_name is now server-computed, no longer validated
- package_type renamed to extension_type throughout
2026-06-19 03:13:31 -05:00
Jonathan Miller c7b6f98f93 feat: add joomla_metadata_validate CLI command (#257)
Validates MokoGitea repo metadata against the actual Joomla extension
manifest XML to catch update delivery mismatches before production.

Checks:
- package_type matches <extension type>
- Element name derived correctly (prefix + lowercase + clean)
- Display name matches <name> tag
- Version consistency (ignoring -dev/-rc suffixes)
- PHP minimum matches composer.json
- Description match (informational)

Supports:
- Local mode: reads .mokogitea/manifest.xml + Joomla XML from disk
- API mode: fetches metadata via Gitea API (--token)
- CI mode: --ci flag exits 1 on errors
- JSON output: --json for workflow integration

Handles all Joomla types: package, component, module, plugin,
template, library, file. Replicates Joomla's InputFilter::clean('cmd')
for element name derivation.

Refs mokoplatform #257
2026-06-19 03:08:03 -05:00
jmiller f8e21dce5a fix(ci): detect rebuild by branch name not version suffix [skip ci] 2026-06-19 01:59:20 +00:00
jmiller 1e4d3d5b3f ci: patch bump on same-branch rebuilds, minor only on elevation [skip ci] 2026-06-19 01:06:59 +00:00
jmiller 78fa142342 Merge pull request 'feat: joomla_metadata_validate CLI command (#257)' (#258) from feature/257-joomla-metadata-validate into main
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Project CI / Lint & Validate (push) Failing after 18s
Platform: mokoplatform CI / Gate 1: Code Quality (push) Failing after 50s
Generic: Project CI / Tests (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokoplatform CI / Gate 4: Governance (push) Has been cancelled
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokoplatform CI / CI Summary (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 20:55:12 +00:00
gitea-actions[bot] 7ac196e4a8 chore(version): auto-bump patch 09.29.01-dev [skip ci]
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Failing after 1s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
2026-06-18 20:53:54 +00:00
Jonathan Miller 2a3e733d8c feat: add joomla_metadata_validate CLI command (#257)
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Site Health (push) Has been skipped
Universal: Auto Version Bump / Version Bump (push) Successful in 8s
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
Validates MokoGitea repo metadata against the actual Joomla extension
manifest XML to catch update delivery mismatches before production.

Checks:
- package_type matches <extension type>
- Element name derived correctly (prefix + lowercase + clean)
- Display name matches <name> tag
- Version consistency (ignoring -dev/-rc suffixes)
- PHP minimum matches composer.json
- Description match (informational)

Supports:
- Local mode: reads .mokogitea/manifest.xml + Joomla XML from disk
- API mode: fetches metadata via Gitea API (--token)
- CI mode: --ci flag exits 1 on errors
- JSON output: --json for workflow integration

Handles all Joomla types: package, component, module, plugin,
template, library, file. Replicates Joomla's InputFilter::clean('cmd')
for element name derivation.

Refs mokoplatform #257
2026-06-18 15:53:43 -05:00
jmiller 3abd08da44 ci: add changelog extraction to promote-rc job [skip ci] 2026-06-18 16:41:04 +00:00
Jonathan Miller 7269800891 fix: restore full workflow_sync.php with PLATFORM_EXCLUDES
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Project CI / Lint & Validate (push) Failing after 17s
Platform: mokoplatform CI / Gate 1: Code Quality (push) Failing after 52s
Generic: Project CI / Tests (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokoplatform CI / Gate 4: Governance (push) Has been cancelled
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokoplatform CI / CI Summary (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 pushes truncated the file content. This commit restores
the complete file with all changes: PLATFORM_EXCLUDES constant,
Phase 1 and Phase 2 exclusion checks, version bump to 09.30.00.
2026-06-18 11:34:59 -05:00
jmiller b038a49279 fix: push full workflow_sync.php with PLATFORM_EXCLUDES (replace truncated content)
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Generic: Project CI / Lint & Validate (push) Failing after 25s
Platform: mokoplatform CI / Gate 1: Code Quality (push) Failing after 38s
Generic: Project CI / Tests (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokoplatform CI / Gate 4: Governance (push) Has been cancelled
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokoplatform CI / CI Summary (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 16:19:28 +00:00
jmiller 9b05cd5fc5 fix: push actual workflow_sync.php with PLATFORM_EXCLUDES (previous commit had placeholder content)
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Project CI / Lint & Validate (push) Failing after 19s
Platform: mokoplatform CI / Gate 1: Code Quality (push) Failing after 54s
Generic: Project CI / Tests (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokoplatform CI / Gate 4: Governance (push) Has been cancelled
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokoplatform CI / CI Summary (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 16:15:29 +00:00
jmiller 2846c361e0 feat: add PLATFORM_EXCLUDES to workflow_sync - skip deploy-manual.yml for Joomla
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Project CI / Lint & Validate (push) Failing after 22s
Platform: mokoplatform CI / Gate 1: Code Quality (push) Failing after 1m4s
Generic: Project CI / Tests (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokoplatform CI / Gate 4: Governance (push) Has been cancelled
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokoplatform CI / CI Summary (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 16:13:40 +00:00
jmiller 2dc43de160 revert: re-enable auto-bump on dev push [skip ci] 2026-06-18 13:22:15 +00:00
jmiller 7ccf2585dd ci: disable auto-bump on push to dev [skip ci] 2026-06-16 19:40:38 +00:00
gitea-actions[bot] ea760bb75b chore(version): pre-release bump to 09.25.03-dev [skip ci] 2026-06-11 20:23:12 +00:00
jmiller d065eaf0fd ci(pre-release): add chore/** branch trigger for pre-release builds
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Universal: Auto Version Bump / Version Bump (push) Failing after 4s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 10s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 1m3s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / 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 20:22:31 +00:00
jmiller 5c31771037 feat(cli): add semver fallback for package.json version bump
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Generic: Project CI / Lint & Validate (push) Failing after 29s
Platform: mokoplatform CI / Gate 1: Code Quality (push) Failing after 1m27s
Generic: Project CI / Tests (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokoplatform CI / Gate 4: Governance (push) Has been cancelled
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokoplatform CI / CI Summary (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
When package.json uses standard x.y.z semver instead of
MokoPlatform XX.YY.ZZ format, auto-bump using the same
bump type (major/minor/patch). Respects --minor and --major flags.
2026-06-11 07:29:20 +00:00
gitea-actions[bot] 8de72a386d chore(release): build 09.29.00 [skip ci] 2026-06-09 19:40:34 +00:00
jmiller 05aafbac61 Merge pull request 'feat: detect display_name, target_version, php_minimum in manifest_detect' (#254) from feat/manifest-detect-update-fields into main 2026-06-09 19:40:08 +00:00
Jonathan Miller 3c57e87066 feat: detect display_name, target_version, php_minimum in manifest_detect
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 3s
Universal: PR Check / Branch Policy (pull_request) Failing after 3s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 4s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: PR Check / Validate PR (pull_request) Failing after 9s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 12s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 5s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || 'development' }}) (pull_request_target) Failing after 21s
Generic: Project CI / Lint & Validate (pull_request) Failing after 34s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 33s
Platform: mokoplatform CI / Gate 1: Code Quality (pull_request) Failing after 1m37s
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: Project CI / Tests (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokoplatform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
These fields are now required by MokoGitea's update feed generator
which reads them from the manifest API (#592).

- display_name: from XML <name> with type prefix detection
- target_version: from <targetplatform version=""> or default (5|6)
- php_minimum: from <php_minimum> tag
2026-06-09 14:39:54 -05:00
gitea-actions[bot] e37443774b chore(release): build 09.28.00 [skip ci] 2026-06-07 22:30:41 +00:00
jmiller e6b9a3b4f6 Merge pull request 'chore: replace MokoJoom references with MokoSuite' (#253) from feat/manifest-integrity into main
Generic: Project CI / Tests (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokoplatform CI / Gate 4: Governance (push) Has been cancelled
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokoplatform CI / CI Summary (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
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Generic: Project CI / Lint & Validate (push) Has been cancelled
Platform: mokoplatform CI / Gate 1: Code Quality (push) Has been cancelled
2026-06-07 22:30:31 +00:00
Jonathan Miller d0200c28f0 chore: replace MokoJoom references with MokoSuite in templates
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: Project CI / Tests (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokoplatform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Generic: Repo Health / Site Health (push) Has been cancelled
Universal: PR Check / Branch Policy (pull_request) Has been cancelled
Generic: Repo Health / Access control (pull_request) Has been cancelled
Generic: Repo Health / Site Health (pull_request) Has been cancelled
Universal: PR Check / Validate PR (pull_request) Has been cancelled
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Has been cancelled
RC Revert / Rename rc/ back to dev/ (pull_request) Has been cancelled
Branch Cleanup / Delete merged branch (pull_request) Has been cancelled
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || 'development' }}) (pull_request_target) Has been cancelled
Generic: Project CI / Lint & Validate (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 1: Code Quality (pull_request) Has been cancelled
Universal: Build & Release / Promote to RC (pull_request) Has been cancelled
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been cancelled
- Makefile: use $(EXTENSION_NAME) variable instead of hardcoded
  mokojoomgallery, update example name to mokosuite
- composer.json: update template package name and description
- mokobackup: rename MokoJoomBackup to MokoSuite Backup in docs/code
2026-06-07 17:26:58 -05:00
gitea-actions[bot] 1f946c0b75 chore(release): build 09.27.00 [skip ci] 2026-06-07 21:30:44 +00:00
jmiller d4e2d36301 Merge pull request 'feat: manifest_integrity.php — org-wide manifest validation' (#252) from feat/manifest-integrity into main
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokoplatform CI / Gate 4: Governance (push) Has been cancelled
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokoplatform CI / CI Summary (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
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Platform: mokoplatform CI / Gate 1: Code Quality (push) Has been cancelled
2026-06-07 21:30:32 +00:00
Jonathan Miller a2c1a61759 feat: add manifest_integrity.php for org-wide manifest validation
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: Project CI / Tests (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokoplatform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Universal: PR Check / Branch Policy (pull_request) Has been cancelled
Generic: Repo Health / Access control (pull_request) Has been cancelled
Generic: Repo Health / Site Health (pull_request) Has been cancelled
Universal: PR Check / Validate PR (pull_request) Has been cancelled
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Has been cancelled
RC Revert / Rename rc/ back to dev/ (pull_request) Has been cancelled
Branch Cleanup / Delete merged branch (pull_request) Has been cancelled
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || 'development' }}) (pull_request_target) Has been cancelled
Generic: Project CI / Lint & Validate (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 1: Code Quality (pull_request) Has been cancelled
Universal: Build & Release / Promote to RC (pull_request) Has been cancelled
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been cancelled
Cross-checks manifest API fields against repo contents.
Three modes: local (--path), single remote (--repo), bulk org.
Validates required fields, platform-specific rules, and version format.
Supports --fix to auto-push corrections, --json for CI consumption.
2026-06-07 16:25:49 -05:00
gitea-actions[bot] 0d12bca60e chore(release): build 09.26.00 [skip ci] 2026-06-07 20:38:11 +00:00
jmiller 7cabc6e5ef Merge pull request 'feat: manifest_detect.php + dev sync + workflow updates' (#251) from feat/manifest-detect into main
Generic: Project CI / Tests (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokoplatform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokoplatform CI / Gate 4: Governance (push) Has been cancelled
Platform: mokoplatform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokoplatform CI / CI Summary (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
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Generic: Project CI / Lint & Validate (push) Has been cancelled
Platform: mokoplatform CI / Gate 1: Code Quality (push) Has been cancelled
2026-06-07 20:37:54 +00:00
Jonathan Miller b73c1eba25 feat: add manifest_detect.php CLI tool for auto-detecting manifest fields
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: Project CI / Tests (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokoplatform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Generic: Repo Health / Site Health (pull_request) Has been cancelled
Universal: PR Check / Branch Policy (pull_request) Has been cancelled
Generic: Repo Health / Access control (pull_request) Has been cancelled
Universal: Build & Release / Promote to RC (pull_request) Has been cancelled
RC Revert / Rename rc/ back to dev/ (pull_request) Has been cancelled
Universal: Security Audit / Dependency Audit (pull_request) Has been cancelled
Branch Cleanup / Delete merged branch (pull_request) Has been cancelled
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Has been cancelled
Universal: PR Check / Validate PR (pull_request) Has been cancelled
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been cancelled
Generic: Project CI / Lint & Validate (pull_request) Has been cancelled
Platform: mokoplatform CI / Gate 1: Code Quality (pull_request) Has been cancelled
Scans source files to detect platform, name, version, element_name,
package_type, language, entry_point, description, and license_spdx.
Supports Joomla, Dolibarr, Go, MCP/Node, and generic platforms.

Includes --diff and --update modes for comparing against and pushing
to the Gitea manifest API. Warns on missing core fields.

Also removes deprecated mcp/servers/mokowaas_api (consolidated to
separate repo) and syncs dev branch changes.
2026-06-07 15:37:24 -05:00
jmiller e5ab1a4cd5 chore: sync security-audit.yml from Template-Generic [skip ci] 2026-06-07 17:55:25 +00:00
jmiller f81873cf37 chore: sync repo-health.yml from Template-Generic [skip ci] 2026-06-07 17:55:24 +00:00
jmiller d9e15b4f4a chore: sync pre-release.yml from Template-Generic [skip ci] 2026-06-07 17:55:23 +00:00
jmiller 31b4bbca2a chore: sync pr-check.yml from Template-Generic [skip ci] 2026-06-07 17:55:23 +00:00
jmiller a7f07b77a2 chore: sync notify.yml from Template-Generic [skip ci] 2026-06-07 17:55:22 +00:00
jmiller 3e84bc9a18 chore: sync issue-branch.yml from Template-Generic [skip ci] 2026-06-07 17:55:21 +00:00
jmiller 7e8c96b629 chore: sync gitleaks.yml from Template-Generic [skip ci] 2026-06-07 17:55:21 +00:00
jmiller 2bf88d1d17 chore: sync deploy-manual.yml from Template-Generic [skip ci] 2026-06-07 17:55:20 +00:00
jmiller 2160cd1ce7 chore: sync cleanup.yml from Template-Generic [skip ci] 2026-06-07 17:55:19 +00:00
jmiller 6b61d9a211 chore: sync ci-generic.yml from Template-Generic [skip ci] 2026-06-07 17:55:19 +00:00
jmiller 1fbffbcb57 chore: sync branch-cleanup.yml from Template-Generic [skip ci] 2026-06-07 17:55:18 +00:00
jmiller f910a8ad12 chore: sync auto-release.yml from Template-Generic [skip ci] 2026-06-07 17:55:17 +00:00
jmiller 5439df3876 chore: sync auto-bump.yml from Template-Generic [skip ci] 2026-06-07 17:55:17 +00:00
Jonathan Miller ab0c209897 Merge branch 'dev' of https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / 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
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
2026-06-07 12:44:31 -05:00
jmiller e4d9bce5d0 docs: update changelog with workflow_sync, platform_detect, and version_prefix
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Universal: Auto Version Bump / Version Bump (push) Successful in 5s
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-07 17:35:58 +00:00
gitea-actions[bot] e933e7b651 chore(version): auto-bump patch 09.25.02-dev [skip ci] 2026-06-07 17:35:06 +00:00
jmiller 157e87279e feat: add version_prefix support to version_bump — prefix-aware find/replace in source files
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / 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
Universal: Auto Version Bump / Version Bump (push) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
2026-06-07 17:35:01 +00:00
gitea-actions[bot] 7850721f86 chore(version): auto-bump patch 09.25.01-dev [skip ci] 2026-06-07 17:34:03 +00:00
jmiller 8949f69699 feat: add version_prefix support — prefix-aware version read and bump
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / 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
Universal: Auto Version Bump / Version Bump (push) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
2026-06-07 17:33:55 +00:00
jmiller af2313d936 feat: add platform_detect.php — auto-detect repo platform and update manifest
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / 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 / Access control (push) Has been cancelled
Generic: Repo Health / Site Health (push) Has been cancelled
Universal: Auto Version Bump / Version Bump (push) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
2026-06-07 17:30:52 +00:00
jmiller 2e5446ff5e feat: add workflow_sync.php — cascading sync based on manifest.platform
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / 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
Universal: Auto Version Bump / Version Bump (push) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
2026-06-07 17:27:49 +00:00
jmiller 17a50851fb fix: gitignore site/ should be /site/ to avoid matching tmpl/site/
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / 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
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
2026-06-07 03:18:38 +00:00
jmiller ef3614d249 fix: gitignore site/ should be /site/ to avoid matching tmpl/site/
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / 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
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
2026-06-07 03:17:58 +00:00
jmiller ab05bb7008 chore: sync .mokogitea/workflows/pre-release.yml from moko-platform [skip ci] 2026-06-06 19:48:11 +00:00
Jonathan Miller 3d5d581883 Merge branch 'dev'
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / 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
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
2026-06-06 13:00:10 -05:00
Jonathan Miller 6bd26698c4 fix: check for manifest_element.php in pre-installed tools validation
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Universal: Auto Version Bump / Version Bump (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
Runner image has stale /opt/moko-platform missing manifest_element.php.
Adding it to the existence check forces a fresh clone until the image
is rebuilt.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-06 12:53:27 -05:00
Jonathan Miller 19b504526b fix: remove double quotes from shell commands in workflow YAML
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / 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
Universal: Auto Version Bump / Version Bump (push) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
act_runner passes run: | blocks through a shell that treats double
quotes as literal characters in some contexts. Removed all double
quotes from echo, test, and git clone commands. Git clone URL is
now built in a variable to avoid quoting issues with the token.

Fixes pre-release and auto-release workflows failing with:
  fatal: protocol '"https' is not supported

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-06 12:44:44 -05:00
Jonathan Miller e7bdf7cbc7 fix: load Composer autoloader in CliFramework constructor (#248)
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / 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
Universal: Auto Version Bump / Version Bump (push) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
CLI tools failed with "Class MokoEnterprise\SourceResolver not found"
because the Composer autoloader was never loaded. The require_once
for CliFramework.php loaded the framework but not the PSR-4 autoloader
that maps MokoEnterprise\ to lib/Enterprise/.

Adding require_once for vendor/autoload.php in the constructor ensures
all Enterprise classes (SourceResolver, etc.) are available to every
CLI tool that extends CliFramework.

Closes #248

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-06 12:34:33 -05:00
Jonathan Miller ff5794d0cc fix: remove dead definitionParser reference in RepositorySynchronizer
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Universal: Auto Version Bump / Version Bump (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
The definitionParser property was never initialized or implemented.
synchronizeRepository() crashed with "Call to a member function
parseForPlatform() on null". Replaced with direct use of
getSharedWorkflows() which provides all files to sync.
2026-06-06 12:09:14 -05:00
Jonathan Miller bd5f676e0a Merge remote-tracking branch 'origin/dev'
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / 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
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
# Conflicts:
#	cli/manifest_licensing.php
2026-06-06 11:49:30 -05:00
Jonathan Miller bfba45e8b5 chore: remove deprecated updates.xml build/sync from workflows
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
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
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Universal: Auto Version Bump / Version Bump (push) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
Generic: Repo Health / Site Health (pull_request) Has been cancelled
Universal: PR Check / Branch Policy (pull_request) Has been cancelled
Generic: Repo Health / Access control (pull_request) Has been cancelled
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Has been cancelled
Universal: PR Check / Validate PR (pull_request) Has been cancelled
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || 'development' }}) (pull_request_target) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Has been cancelled
Branch Cleanup / Delete merged branch (pull_request) Has been cancelled
Universal: Build & Release / Promote to RC (pull_request) Has been cancelled
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been cancelled
MokoGitea generates update feeds dynamically from releases.
Static updates.xml is no longer needed.
2026-06-06 11:24:03 -05:00
Jonathan Miller 78ea05233b fix: update workflow path comments and token references
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / 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
Universal: Auto Version Bump / Version Bump (push) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
- Replace .gitea/ with .mokogitea/ in PATH comments
- Standardize token names to MOKOGITEA_TOKEN
- Remove github.token fallback patterns
2026-06-06 11:11:10 -05:00
Jonathan Miller ae0d54310d fix: replace smart quotes with ASCII in pre-release.yml (#245)
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / 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
Universal: Auto Version Bump / Version Bump (push) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
Unicode smart quotes (U+201C/U+201D) in the Setup moko-platform tools
step caused `fatal: protocol '"https' is not supported` during git clone.

Closes #245

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-06 10:23:42 -05:00
Jonathan Miller 9df59836bf chore: update CLAUDE.md template with first-run setup and focused format
Replace verbose boilerplate with platform-specific scaffold including
first-run setup checklist and placeholder tokens for new repos.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-06 10:23:42 -05:00
Jonathan Miller 6e40707223 chore: move CLAUDE.md to .mokogitea/ directory
Relocate CLAUDE.md from repo root to .mokogitea/ per project convention.
Content updated with focused, repo-specific architecture and rules.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-06 10:23:41 -05:00
Jonathan Miller ca55e5d2d2 feat(core): add SourceResolver for backwards-compatible src/ → source/ migration
Introduces SourceResolver utility class with source/ → src/ → htdocs/
fallback chain, replacing hardcoded src/ references across 28 files.
This enables renaming root-level src/ to source/ in all repos while
maintaining backwards compatibility during the transition.

Phase 1: New lib/Enterprise/SourceResolver.php with resolve(),
resolveAbsolute(), globSource(), findUnderSource(), warnIfLegacy()
Phase 2: Updated 19 CLI/deploy tools to use SourceResolver
Phase 3: Updated 7 validator/lib files (McpServerPlugin,
PackageBuilder, RepositorySynchronizer, auto_detect_platform,
check_dolibarr_module, check_client_theme, check_structure)

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-06 10:23:41 -05:00
jmiller 9526d006c4 feat(ci): add manifest_licensing step to pre-release workflow
Ensures updateservers, dlid, and blockChildUninstall tags are
present in Joomla extension manifests when licensing is enabled.

Authored-by: Moko Consulting
2026-06-06 10:23:41 -05:00
Jonathan Miller c90a5671bd feat(cli): add manifest_licensing.php for update server and dlid management
New CLI tool that reads <licensing> from manifest.xml and ensures
Joomla extension manifests have correct updateservers, dlid, and
blockChildUninstall tags. Supports dry-run and --fix modes.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-06 10:23:40 -05:00
gitea-actions[bot] 048a7d71d1 chore(release): build 09.25.00 [skip ci] 2026-06-06 10:23:40 -05:00
jmiller 847263dd86 feat(ci): add manifest_licensing step to pre-release workflow
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / 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
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
Ensures updateservers, dlid, and blockChildUninstall tags are
present in Joomla extension manifests when licensing is enabled.

Authored-by: Moko Consulting
2026-06-06 12:27:19 +00:00
Jonathan Miller 6e540f64c4 feat(cli): add manifest_licensing.php for update server and dlid management
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / 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
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
New CLI tool that reads <licensing> from manifest.xml and ensures
Joomla extension manifests have correct updateservers, dlid, and
blockChildUninstall tags. Supports dry-run and --fix modes.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-06 07:19:19 -05:00
jmiller c57b5724ac chore: remove update-server docs [skip ci] 2026-06-05 00:55:13 +00:00
jmiller 78affd37ff chore: remove update-server docs [skip ci] 2026-06-05 00:55:12 +00:00
jmiller b3062c6559 chore: remove update-server docs [skip ci] 2026-06-05 00:55:11 +00:00
jmiller cf02738930 chore: remove update-server docs [skip ci] 2026-06-05 00:55:10 +00:00
jmiller 455d4c8a19 chore: remove update-server docs [skip ci] 2026-06-05 00:55:09 +00:00
jmiller 8286d493b9 chore: remove update-server docs [skip ci] 2026-06-05 00:55:08 +00:00
jmiller b740152d67 Merge pull request 'ci: pre-installed /opt/moko-platform on runner' (#244) from dev into main
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (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-04 23:43:57 +00:00
Jonathan Miller 9dab9f1ef6 ci: use pre-installed /opt/moko-platform on runner, fallback to clone
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Universal: Auto Version Bump / Version Bump (push) Has been cancelled
Universal: PR Check / Branch Policy (pull_request) Has been cancelled
Generic: Repo Health / Access control (pull_request) Has been cancelled
Generic: Repo Health / Site Health (pull_request) Has been cancelled
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Has been cancelled
Universal: PR Check / Validate PR (pull_request) Has been cancelled
Branch Cleanup / Delete merged branch (pull_request) Has been cancelled
Universal: Build & Release / Promote to RC (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
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
All workflows check for /opt/moko-platform first (updated by cron
every 6h). Falls back to fresh clone if not available.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 18:43:03 -05:00
Jonathan Miller c61d32709c chore: remove deprecated update-server.yml workflow [skip ci]
Authored-by: Moko Consulting
2026-06-04 18:42:33 -05:00
gitea-actions[bot] a59091e348 chore(release): build 09.25.00 [skip ci] 2026-06-04 23:03:39 +00:00
jmiller ee3d58c20f Merge pull request 'ci: remove updates.xml workflow steps, use CLI version_bump for pre-release' (#243) from dev into main
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / 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-04 23:03:26 +00:00
Jonathan Miller 2b137f9041 Merge remote-tracking branch 'origin/main' into dev
Generic: Repo Health / Site Health (pull_request) Has been cancelled
Universal: PR Check / Branch Policy (pull_request) Has been cancelled
Generic: Repo Health / Access control (pull_request) Has been cancelled
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Has been cancelled
Universal: Auto Version Bump / Version Bump (push) Has been cancelled
Universal: PR Check / Validate PR (pull_request) Has been cancelled
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || 'development' }}) (pull_request_target) Has been cancelled
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Has been cancelled
Branch Cleanup / Delete merged branch (pull_request) Has been cancelled
Universal: Build & Release / Promote to RC (pull_request) Has been cancelled
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
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
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
# Conflicts:
#	.mokogitea/workflows/pre-release.yml
#	CHANGELOG.md
2026-06-04 18:02:23 -05:00
gitea-actions[bot] 54a27c0a8f chore(release): build 09.24.00 [skip ci] 2026-06-04 16:29:58 +00:00
jmiller 5754fae5a8 Merge pull request 'feat: --skip-update-stream flag + CI workflow updates' (#227) from dev into main
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
feat: --skip-update-stream flag + CI workflow updates
2026-06-04 16:29:47 +00:00
gitea-actions[bot] ab3c0a3a8d fix: ensure all pre-releases marked prerelease=true [skip ci] 2026-06-04 16:10:52 +00:00
jmiller eb3689cff6 chore: sync .mokogitea/workflows/pr-check.yml from moko-platform [skip ci] 2026-06-04 15:58:05 +00:00
jmiller 7338a3da2e chore: sync .mokogitea/workflows/pr-check.yml from moko-platform [skip ci] 2026-06-04 15:40:31 +00:00
jmiller 0a0e1f11e0 docs: add Dolibarr Update Server standards [skip ci] 2026-06-04 15:39:23 +00:00
jmiller c3a3ab3f62 docs: add Joomla Update Server standards [skip ci] 2026-06-04 15:38:29 +00:00
jmiller 556ac85a63 chore: remove static updates.xml.template - now served dynamically [skip ci] 2026-06-04 15:36:21 +00:00
jmiller c1a145480c chore: sync .mokogitea/workflows/pr-check.yml from moko-platform [skip ci] 2026-06-04 15:31:53 +00:00
jmiller ab7b6cfba1 chore: sync .mokogitea/workflows/pr-check.yml from moko-platform [skip ci] 2026-06-04 15:18:43 +00:00
gitea-actions[bot] 2d6155d655 chore: sync pre-release workflow — auto RC on PR to main [skip ci] 2026-06-04 14:45:48 +00:00
jmiller 65215cdc4c chore: sync .mokogitea/workflows/auto-release.yml from moko-platform [skip ci] 2026-06-04 14:23:09 +00:00
jmiller 8c87cf1e74 chore: sync .mokogitea/workflows/repo-health.yml from moko-platform [skip ci] 2026-06-04 13:46:56 +00:00
Jonathan Miller 59d3524615 feat(ci): auto-file Gitea issues when CI gates fail
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / Release configuration (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
Add ci-issue-reporter.sh shared script that creates or updates issues
via the Gitea API with dedup (searches open issues by ci-auto label).
Integrated into ci-platform, repo-health, and pr-check workflows.

Also fix push_files.php: getFileContent() → getFileContents() with
base64 decode to match the MokoGiteaAdapter interface.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-02 15:42:04 -05:00
Jonathan Miller 8058baef95 feat(cli): detect src/ entry-point in Joomla package sub-extensions
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / Release configuration (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
When building a Joomla package, sub-package directories that contain a
src/ folder with a Joomla manifest XML (e.g. git submodules of full
repos) now zip src/ instead of the repo root. This avoids bundling
README, CI config, and other repo-level files into extension packages.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-02 10:10:40 -05:00
Jonathan Miller df2efa4838 Merge fix/cli-banner-stderr: route all decorative CLI output to stderr
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / Release configuration (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Properly fixes the root cause that b53846f worked around — instead of
adding --quiet flags to individual callers, all CliFramework display
methods now write to stderr via display(), keeping stdout clean for
machine-readable data in all CLI scripts.

Authored-by: Moko Consulting
2026-06-02 10:07:34 -05:00
Jonathan Miller 76bc91a383 fix: route all decorative CLI output to stderr
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Generic: Repo Health / Release configuration (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
All display methods (banner, progress, section, status, log, table,
summary box, divider, step) now write to stderr via a new display()
helper. This ensures stdout is reserved for machine-readable data,
fixing CI pipelines that capture CLI output with $() or redirect to
$GITHUB_OUTPUT.

Root cause: version_read.php banner was written to stdout, so
  VERSION=$(php version_read.php --path .)
captured the box-drawing characters along with the version string,
corrupting $GITHUB_OUTPUT and breaking downstream release steps.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-02 10:01:16 -05:00
Jonathan Miller b53846f6f4 fix: prevent version_read banner from corrupting XML manifests
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / Release configuration (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
- Add --quiet flag to version_read.php call in version_auto_bump.php
  so the CliFramework banner doesn't pollute stdout
- Parse version output by matching XX.YY.ZZ pattern instead of
  blindly taking the first line
- Add version format validation in version_set_platform.php to reject
  non-XX.YY.ZZ values before writing to XML files

Root cause: exec() captured the decorative banner output from
version_read.php and version_set_platform.php's regex replacement
injected it into <version> tags across all Joomla manifests.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-02 09:01:19 -05:00
694 changed files with 84317 additions and 4471 deletions
+12
View File
@@ -0,0 +1,12 @@
[submodule "templates/repos/Template-Client"]
path = templates/repos/Template-Client
url = https://git.mokoconsulting.tech/MokoConsulting/Template-Client.git
[submodule "templates/repos/Template-Generic"]
path = templates/repos/Template-Generic
url = https://git.mokoconsulting.tech/MokoConsulting/Template-Generic.git
[submodule "templates/repos/Template-Joomla"]
path = templates/repos/Template-Joomla
url = https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla.git
[submodule "templates/repos/Template-MCP"]
path = templates/repos/Template-MCP
url = https://git.mokoconsulting.tech/MokoConsulting/Template-MCP.git
+88
View File
@@ -0,0 +1,88 @@
<<<<<<< HEAD
# MokoCLI
=======
# mokoplatform
>>>>>>> main
Enterprise automation, validation, sync, and governance engine for all Moko Consulting repositories.
## Quick Reference
| Field | Value |
|---|---|
| **Language** | PHP 8.1+ |
| **Version** | 09.01.00 |
| **Branch** | develop on `dev`, merge to `main` (protected) |
<<<<<<< HEAD
| **Wiki** | [MokoCLI Wiki](https://git.mokoconsulting.tech/MokoConsulting/mokocli/wiki) |
=======
| **Wiki** | [mokoplatform Wiki](https://git.mokoconsulting.tech/MokoConsulting/mokoplatform/wiki) |
>>>>>>> main
## Commands
```bash
composer install # Install PHP dependencies
php bin/moko health --path . # Repo health check
php bin/moko check:syntax --path . # PHP syntax check
php bin/moko drift --org MokoConsulting # Scan for standards drift
php bin/moko dashboard --token $TOKEN -o dashboard.html # Client dashboard
# Code quality
php vendor/bin/phpcs --standard=phpcs.xml -n lib/ validate/ automation/ cli/
php vendor/bin/phpcbf --standard=phpcs.xml lib/ validate/ automation/ cli/
php vendor/bin/phpstan analyse -c phpstan.neon --memory-limit=512M
composer check # Run all checks
```
## Architecture
| Directory | Purpose |
|---|---|
| `cli/` | 32 standalone CLI tools (version, release, build, repo management) |
| `validate/` | 20 validation scripts (syntax, structure, manifests, drift) |
| `automation/` | 7 bulk operations (sync, push files, templates, cleanup) |
| `lib/Enterprise/` | Core library — CliFramework, ApiClient, adapters, validators |
| `lib/Enterprise/Plugins/` | 11 platform plugins (Joomla, Dolibarr, Node.js, Python, etc.) |
| `deploy/` | SFTP deployment scripts (Joomla, Dolibarr, health checks) |
| `templates/` | Universal templates, configs, governance schema |
| `.mokogitea/workflows/` | CI/CD workflows (Gitea Actions) |
| `bin/moko` | Unified CLI dispatcher — `php bin/moko <command>` |
| `monitoring/sites.json` | Sites list for mcp_mokomonitor |
### CLI Framework
All CLI tools extend `MokoCli\CliFramework` (`lib/Enterprise/CliFramework.php`).
Built-in flags: `--help`, `--verbose`, `--quiet`, `--dry-run`.
After adding a CLI tool, register it in `bin/moko` COMMAND_MAP.
### Platform Adapters
- `MokoGiteaAdapter` — git.mokoconsulting.tech (primary)
- `GitHubAdapter` — github.com mirrors
### Plugin System
Platform-specific logic in `lib/Enterprise/Plugins/`. Each implements `ProjectPluginInterface` with health checks, validation, build commands, config schemas.
## Code Quality
| Tool | Level | Config |
|---|---|---|
| PHPCS | PSR-12 (errors only) | `phpcs.xml` |
| PHPStan | Level 2 (advisory) | `phpstan.neon` |
PHPStan runs with `--memory-limit=512M`. CI enforces PHPCS errors; PHPStan is `continue-on-error`.
## Rules
- **Never commit** `.claude/`, `.mcp.json`, `TODO.md`, `*.min.css`/`*.min.js`
- **Attribution**: `Authored-by: Moko Consulting`
- **Workflow directory**: `.mokogitea/` (not `.gitea/` or `.github/`)
- **Wiki**: documentation lives in the Gitea wiki, not `docs/` files
- **New CLI tools**: extend `CliFramework`, not `CLIApp` (legacy)
<<<<<<< HEAD
- **Standards**: [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/mokocli/wiki/Home)
=======
- **Standards**: [MokoCli](https://git.mokoconsulting.tech/MokoConsulting/mokoplatform/wiki/Home)
>>>>>>> main
+7 -2
View File
@@ -7,8 +7,13 @@ contact_links:
- name: 💬 Ask a Question - name: 💬 Ask a Question
url: https://mokoconsulting.tech/ url: https://mokoconsulting.tech/
about: Get help or ask questions through our website about: Get help or ask questions through our website
- name: 📚 moko-platform Documentation <<<<<<< HEAD
url: https://git.mokoconsulting.tech/MokoConsulting/moko-platform - name: 📚 MokoCLI Documentation
url: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
- name: 📚 mokoplatform Documentation
url: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
about: View our coding standards and best practices about: View our coding standards and best practices
- name: 🔒 Report a Security Vulnerability - name: 🔒 Report a Security Vulnerability
url: https://git.mokoconsulting.tech/mokoconsulting-tech/.github-private/security/advisories/new url: https://git.mokoconsulting.tech/mokoconsulting-tech/.github-private/security/advisories/new
+5 -1
View File
@@ -42,7 +42,11 @@ Suggested text here
<!-- Add any other context, screenshots, or references --> <!-- Add any other context, screenshots, or references -->
## Standards Alignment ## Standards Alignment
- [ ] Follows moko-platform documentation guidelines <<<<<<< HEAD
- [ ] Follows MokoCLI documentation guidelines
=======
- [ ] Follows mokoplatform documentation guidelines
>>>>>>> main
- [ ] Uses en_US/en_GB localization - [ ] Uses en_US/en_GB localization
- [ ] Includes proper SPDX headers where applicable - [ ] Includes proper SPDX headers where applicable
+5 -1
View File
@@ -37,7 +37,11 @@ If you have ideas about how this could be implemented, share them here:
Add any other context, mockups, or screenshots about the feature request here. Add any other context, mockups, or screenshots about the feature request here.
## Relevant Standards ## Relevant Standards
Does this relate to any standards in [moko-platform](https://git.mokoconsulting.tech/MokoConsulting/moko-platform)? <<<<<<< HEAD
Does this relate to any standards in [MokoCLI](https://git.mokoconsulting.tech/MokoConsulting/mokocli)?
=======
Does this relate to any standards in [mokoplatform](https://git.mokoconsulting.tech/MokoConsulting/mokoplatform)?
>>>>>>> main
- [ ] Accessibility (WCAG 2.1 AA) - [ ] Accessibility (WCAG 2.1 AA)
- [ ] Localization (en_US/en_GB) - [ ] Localization (en_US/en_GB)
- [ ] Security best practices - [ ] Security best practices
+5 -1
View File
@@ -35,7 +35,11 @@ Use this template only for:
<!-- Describe how this could be addressed --> <!-- Describe how this could be addressed -->
## Standards Reference ## Standards Reference
Does this relate to security standards in [moko-platform](https://git.mokoconsulting.tech/MokoConsulting/moko-platform)? <<<<<<< HEAD
Does this relate to security standards in [MokoCLI](https://git.mokoconsulting.tech/MokoConsulting/mokocli)?
=======
Does this relate to security standards in [mokoplatform](https://git.mokoconsulting.tech/MokoConsulting/mokoplatform)?
>>>>>>> main
- [ ] SPDX license identifiers - [ ] SPDX license identifiers
- [ ] Secret management - [ ] Secret management
- [ ] Dependency security - [ ] Dependency security
+15 -5
View File
@@ -2,8 +2,13 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Automation <<<<<<< HEAD
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # INGROUP: MokoCLI.Automation
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
# INGROUP: mokoplatform.Automation
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
# PATH: /.gitea/workflows/branch-protection.yml # PATH: /.gitea/workflows/branch-protection.yml
# BRIEF: Apply standardised branch protection rules to all governed repositories # BRIEF: Apply standardised branch protection rules to all governed repositories
# #
@@ -57,13 +62,18 @@ jobs:
- name: Determine target repos - name: Determine target repos
id: repos id: repos
env: env:
GA_TOKEN: ${{ secrets.GA_TOKEN }} GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
run: | run: |
API="${GITEA_URL}/api/v1" API="${GITEA_URL}/api/v1"
# Platform/standards/infra repos to exclude # Platform/standards/infra repos to exclude
EXCLUDE="gitea-org-config org-profile gitea-private .mokogitea-private moko-platform MokoTesting" <<<<<<< HEAD
EXCLUDE="gitea-org-config org-profile gitea-private .mokogitea-private mokocli mokoplatform MokoTesting"
EXCLUDE="$EXCLUDE MokoStandards-Template-Client MokoStandards-Template-Dolibarr MokoStandards-Template-Generic MokoStandards-Template-Joomla MokoDoliProjTemplate" EXCLUDE="$EXCLUDE MokoStandards-Template-Client MokoStandards-Template-Dolibarr MokoStandards-Template-Generic MokoStandards-Template-Joomla MokoDoliProjTemplate"
=======
EXCLUDE="gitea-org-config org-profile gitea-private .mokogitea-private mokoplatform MokoTesting"
EXCLUDE="$EXCLUDE MokoCli-Template-Client MokoCli-Template-Dolibarr MokoCli-Template-Generic MokoCli-Template-Joomla MokoDoliProjTemplate"
>>>>>>> main
if [ -n "${{ inputs.repos }}" ]; then if [ -n "${{ inputs.repos }}" ]; then
# User-specified repos # User-specified repos
@@ -105,7 +115,7 @@ jobs:
- name: Apply protection rules - name: Apply protection rules
env: env:
GA_TOKEN: ${{ secrets.GA_TOKEN }} GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
DRY_RUN: ${{ inputs.dry_run || 'false' }} DRY_RUN: ${{ inputs.dry_run || 'false' }}
run: | run: |
API="${GITEA_URL}/api/v1" API="${GITEA_URL}/api/v1"
+10 -5
View File
@@ -2,8 +2,13 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Automation <<<<<<< HEAD
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # INGROUP: MokoCLI.Automation
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
# INGROUP: mokoplatform.Automation
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
# PATH: /.gitea/workflows/bulk-repo-sync.yml # PATH: /.gitea/workflows/bulk-repo-sync.yml
# BRIEF: Bulk repo sync — runs from API repo, syncs standards to all governed repos # BRIEF: Bulk repo sync — runs from API repo, syncs standards to all governed repos
@@ -84,8 +89,8 @@ jobs:
echo "Running: php automation/bulk_sync.php ${{ steps.args.outputs.args }}" echo "Running: php automation/bulk_sync.php ${{ steps.args.outputs.args }}"
php automation/bulk_sync.php ${{ steps.args.outputs.args }} 2>&1 | tee /tmp/bulk_sync.log php automation/bulk_sync.php ${{ steps.args.outputs.args }} 2>&1 | tee /tmp/bulk_sync.log
env: env:
GA_TOKEN: ${{ secrets.GA_TOKEN }} GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
GH_TOKEN: ${{ secrets.GH_TOKEN }} GH_TOKEN: ${{ secrets.GH_PAT }}
GIT_PLATFORM: gitea GIT_PLATFORM: gitea
GITEA_URL: https://git.mokoconsulting.tech GITEA_URL: https://git.mokoconsulting.tech
GITEA_ORG: MokoConsulting GITEA_ORG: MokoConsulting
@@ -112,7 +117,7 @@ jobs:
bash automation/enforce_tags.sh || echo "Tag enforcement had errors (non-fatal)" bash automation/enforce_tags.sh || echo "Tag enforcement had errors (non-fatal)"
fi fi
env: env:
GA_TOKEN: ${{ secrets.GA_TOKEN }} GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
GITEA_URL: https://git.mokoconsulting.tech GITEA_URL: https://git.mokoconsulting.tech
GITEA_ORG: MokoConsulting GITEA_ORG: MokoConsulting
+9 -3
View File
@@ -2,9 +2,15 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: moko-platform.CI <<<<<<< HEAD
# INGROUP: moko-platform # DEFGROUP: MokoCLI.CI
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # INGROUP: MokoCLI
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
# DEFGROUP: mokoplatform.CI
# INGROUP: mokoplatform
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
# PATH: /.gitea/workflows/pr-branch-check.yml # PATH: /.gitea/workflows/pr-branch-check.yml
# BRIEF: PR branch merge policy enforcement # BRIEF: PR branch merge policy enforcement
# #
+15 -5
View File
@@ -4,8 +4,13 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Automation <<<<<<< HEAD
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # INGROUP: MokoCLI.Automation
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
# INGROUP: mokoplatform.Automation
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
# PATH: /.gitea/workflows/renovate.yml # PATH: /.gitea/workflows/renovate.yml
# BRIEF: Run Renovate Bot across all governed repos for dependency updates # BRIEF: Run Renovate Bot across all governed repos for dependency updates
# #
@@ -57,12 +62,17 @@ jobs:
- name: Determine target repos - name: Determine target repos
id: repos id: repos
env: env:
GA_TOKEN: ${{ secrets.GA_TOKEN }} GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
run: | run: |
API="${GITEA_URL}/api/v1" API="${GITEA_URL}/api/v1"
EXCLUDE="gitea-org-config org-profile gitea-private .mokogitea-private moko-platform MokoTesting" <<<<<<< HEAD
EXCLUDE="gitea-org-config org-profile gitea-private .mokogitea-private mokocli mokoplatform MokoTesting"
EXCLUDE="$EXCLUDE MokoStandards-Template-Client MokoStandards-Template-Dolibarr MokoStandards-Template-Generic MokoStandards-Template-Joomla MokoDoliProjTemplate" EXCLUDE="$EXCLUDE MokoStandards-Template-Client MokoStandards-Template-Dolibarr MokoStandards-Template-Generic MokoStandards-Template-Joomla MokoDoliProjTemplate"
=======
EXCLUDE="gitea-org-config org-profile gitea-private .mokogitea-private mokoplatform MokoTesting"
EXCLUDE="$EXCLUDE MokoCli-Template-Client MokoCli-Template-Dolibarr MokoCli-Template-Generic MokoCli-Template-Joomla MokoDoliProjTemplate"
>>>>>>> main
if [ -n "${{ inputs.repos }}" ]; then if [ -n "${{ inputs.repos }}" ]; then
REPOS=$(echo "${{ inputs.repos }}" | tr ',' ' ') REPOS=$(echo "${{ inputs.repos }}" | tr ',' ' ')
@@ -107,7 +117,7 @@ jobs:
- name: Run Renovate - name: Run Renovate
if: steps.repos.outputs.repo_list != '' if: steps.repos.outputs.repo_list != ''
env: env:
RENOVATE_TOKEN: ${{ secrets.GA_TOKEN }} RENOVATE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
RENOVATE_PLATFORM: gitea RENOVATE_PLATFORM: gitea
RENOVATE_ENDPOINT: ${{ env.GITEA_URL }}/api/v1 RENOVATE_ENDPOINT: ${{ env.GITEA_URL }}/api/v1
RENOVATE_GIT_AUTHOR: 'Renovate Bot <renovate@mokoconsulting.tech>' RENOVATE_GIT_AUTHOR: 'Renovate Bot <renovate@mokoconsulting.tech>'
+8 -3
View File
@@ -4,8 +4,13 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Maintenance <<<<<<< HEAD
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # INGROUP: MokoCLI.Maintenance
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
# INGROUP: mokoplatform.Maintenance
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
# PATH: /.gitea/workflows/sync-wikis.yml # PATH: /.gitea/workflows/sync-wikis.yml
# BRIEF: Daily sync of all Gitea wikis to consolidated GitHub wiki repo # BRIEF: Daily sync of all Gitea wikis to consolidated GitHub wiki repo
@@ -31,7 +36,7 @@ jobs:
- name: Sync all wikis - name: Sync all wikis
env: env:
GH_TOKEN: ${{ secrets.GH_TOKEN }} GH_TOKEN: ${{ secrets.GH_PAT }}
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
run: | run: |
if [ -z "$GH_TOKEN" ]; then if [ -z "$GH_TOKEN" ]; then
+33 -9
View File
@@ -4,10 +4,17 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Release <<<<<<< HEAD
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # INGROUP: MokoCLI.Release
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.mokogitea/workflows/auto-bump.yml # PATH: /.mokogitea/workflows/auto-bump.yml
# VERSION: 09.23.00 # VERSION: 09.23.00
=======
# INGROUP: mokocli.Release
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.mokogitea/workflows/auto-bump.yml
# VERSION: 09.02.00
>>>>>>> main
# BRIEF: Auto patch-bump version on every push to dev (skips merge commits) # BRIEF: Auto patch-bump version on every push to dev (skips merge commits)
name: "Universal: Auto Version Bump" name: "Universal: Auto Version Bump"
@@ -43,19 +50,36 @@ jobs:
token: ${{ secrets.MOKOGITEA_TOKEN }} token: ${{ secrets.MOKOGITEA_TOKEN }}
fetch-depth: 1 fetch-depth: 1
- name: Setup moko-platform tools <<<<<<< HEAD
- name: Setup MokoCLI tools
run: |
# Check both new (mokocli) and legacy (mokoplatform) install paths
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"
elif [ -f "/opt/mokoplatform/cli/version_bump.php" ] && [ -f "/opt/mokoplatform/vendor/autoload.php" ]; then
echo "Using pre-installed /opt/mokoplatform (legacy path)"
echo "MOKO_CLI=/opt/mokoplatform/cli" >> "$GITHUB_ENV"
else
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
rm -rf /tmp/mokocli
=======
- name: Setup mokocli tools
run: | run: |
if ! command -v composer &> /dev/null; then if ! command -v composer &> /dev/null; then
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1
fi fi
if [ -d "/opt/moko-platform/cli" ]; then if [ -d "/opt/mokocli/cli" ]; then
echo "MOKO_CLI=/opt/moko-platform/cli" >> "$GITHUB_ENV" echo "MOKO_CLI=/opt/mokocli/cli" >> "$GITHUB_ENV"
else else
>>>>>>> main
git clone --depth 1 --branch main --quiet \ git clone --depth 1 --branch main --quiet \
"https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/moko-platform.git" \ "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/mokocli.git" \
/tmp/moko-platform-api /tmp/mokocli
cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet cd /tmp/mokocli && composer install --no-dev --no-interaction --quiet
echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV" echo "MOKO_CLI=/tmp/mokocli/cli" >> "$GITHUB_ENV"
fi fi
- name: Bump version - name: Bump version
+493 -285
View File
@@ -1,285 +1,493 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech> # Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
# #
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Release <<<<<<< HEAD
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform # INGROUP: MokoCLI.Release
# PATH: /templates/workflows/universal/auto-release.yml.template # REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# VERSION: 05.00.00 =======
# BRIEF: Universal build & release detects platform from manifest.xml # INGROUP: mokocli.Release
# # REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/mokocli
# +========================================================================+ >>>>>>> main
# | UNIVERSAL BUILD & RELEASE PIPELINE | # PATH: /templates/workflows/universal/auto-release.yml.template
# +========================================================================+ # VERSION: 05.00.00
# | | # BRIEF: Universal build & release detects platform from manifest.xml
# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. | #
# | | # +========================================================================+
# | Platform-specific: | # | UNIVERSAL BUILD & RELEASE PIPELINE |
# | joomla: XML manifest, updates.xml, type-prefixed packages | # +========================================================================+
# | dolibarr: mod*.class.php, update.txt, dev version reset | # | |
# | generic: README-only, no update stream | # | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. |
# | | # | |
# +========================================================================+ # | Platform-specific: |
# | joomla: XML manifest, type-prefixed packages |
name: "Universal: Build & Release" # | dolibarr: mod*.class.php, update.txt, dev version reset |
# | generic: README-only, no update stream |
on: # | |
pull_request: # +========================================================================+
types: [opened, closed]
branches: name: "Universal: Build & Release"
- main
workflow_dispatch: on:
inputs: pull_request:
action: types: [opened, closed]
description: 'Action to perform' branches:
required: false - main
type: choice workflow_dispatch:
default: release inputs:
options: action:
- release description: 'Action to perform'
- promote-rc required: false
type: choice
env: default: release
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true options:
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} - release
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} - promote-rc
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
env:
permissions: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
contents: write GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
jobs: GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
# ── PR Opened → Rename branch to RC and build RC release ─────────────────────
promote-rc: permissions:
name: Promote to RC contents: write
runs-on: release
if: >- jobs:
(github.event.action == 'opened' && github.event.pull_request.merged != true) || # ── PR Opened → Rename branch to RC and build RC release ─────────────────────
(github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc') promote-rc:
name: Promote to RC
steps: runs-on: release
- name: Checkout repository if: >-
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 (github.event.action == 'opened' && github.event.pull_request.merged != true) ||
with: (github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc')
token: ${{ secrets.MOKOGITEA_TOKEN }}
fetch-depth: 1 steps:
- name: Checkout repository
- name: Setup moko-platform tools uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
env: with:
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} token: ${{ secrets.MOKOGITEA_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting fetch-depth: 1
run: |
if ! command -v composer &> /dev/null; then <<<<<<< HEAD
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 - name: Setup MokoCLI tools
fi =======
# Always fetch latest CLI tools — never use stale cache from previous runs - name: Setup mokocli tools
rm -rf /tmp/moko-platform-api >>>>>>> main
git clone --depth 1 --branch main --quiet \ env:
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
/tmp/moko-platform-api MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
cd /tmp/moko-platform-api run: |
composer install --no-dev --no-interaction --quiet <<<<<<< HEAD
# Check both new (mokocli) and legacy (mokoplatform) install paths
- name: Rename branch to rc if [ -f /opt/mokocli/cli/version_bump.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then
run: | echo "Using pre-installed /opt/mokocli"
php /tmp/moko-platform-api/cli/branch_rename.php \ echo MOKO_CLI=/opt/mokocli/cli >> $GITHUB_ENV
--from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \ elif [ -f /opt/mokoplatform/cli/version_bump.php ] && [ -f /opt/mokoplatform/vendor/autoload.php ]; then
--token "${{ secrets.MOKOGITEA_TOKEN }}" \ echo "Using pre-installed /opt/mokoplatform (legacy path)"
--api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \ echo MOKO_CLI=/opt/mokoplatform/cli >> $GITHUB_ENV
--pr "${{ github.event.pull_request.number }}" else
echo "Falling back to fresh clone"
- name: Checkout rc and configure git =======
run: | if [ -f /opt/mokocli/cli/version_bump.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then
git fetch origin rc echo Using pre-installed /opt/mokocli
git checkout rc echo MOKO_CLI=/opt/mokocli/cli >> $GITHUB_ENV
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" else
git config --local user.name "gitea-actions[bot]" echo Falling back to fresh clone
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" >>>>>>> main
if ! command -v composer > /dev/null 2>&1; then
- name: Publish RC release 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
run: | fi
php /tmp/moko-platform-api/cli/release_publish.php \ rm -rf /tmp/mokocli
--path . --stability rc --bump minor --branch rc \ CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/mokocli.git
--token "${{ secrets.MOKOGITEA_TOKEN }}" \ git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/mokocli
--skip-update-stream cd /tmp/mokocli
composer install --no-dev --no-interaction --quiet
- name: Summary echo MOKO_CLI=/tmp/mokocli/cli >> $GITHUB_ENV
if: always() fi
run: |
echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY - name: Rename branch to rc
echo "Branch renamed to rc, minor bump, RC release built (updates.xml managed by Gitea Pages)" >> $GITHUB_STEP_SUMMARY run: |
php ${MOKO_CLI}/branch_rename.php \
# ── Merged PR → Build & Release (or promote RC to stable) ──────────────────── --from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \
release: --token "${{ secrets.MOKOGITEA_TOKEN }}" \
name: Build & Release Pipeline --api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \
runs-on: release --pr "${{ github.event.pull_request.number }}"
if: >-
github.event.pull_request.merged == true || - name: Checkout rc and configure git
(github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc') run: |
git fetch origin rc
steps: git checkout rc
- name: Checkout repository git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 git config --local user.name "gitea-actions[bot]"
with: git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
token: ${{ secrets.MOKOGITEA_TOKEN }}
fetch-depth: 0 - name: Publish RC release
run: |
- name: Configure git for bot pushes php ${MOKO_CLI}/release_publish.php \
run: | --path . --stability rc --bump minor --branch rc \
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" --token "${{ secrets.MOKOGITEA_TOKEN }}"
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" <<<<<<< HEAD
=======
- name: Check for merge conflict markers - name: Update RC release notes from CHANGELOG.md
run: | run: |
CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true) API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
if [ -n "$CONFLICTS" ]; then TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
echo "::error::Merge conflict markers found — aborting release"
echo "## Release Blocked: Conflict Markers" >> $GITHUB_STEP_SUMMARY # Extract [Unreleased] section from changelog
echo '```' >> $GITHUB_STEP_SUMMARY NOTES=""
echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY if [ -f "CHANGELOG.md" ]; then
echo '```' >> $GITHUB_STEP_SUMMARY NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md)
exit 1 fi
fi [ -z "$NOTES" ] && NOTES="Release candidate"
echo "No conflict markers found"
# Find the RC release and update its body
- name: Setup moko-platform tools RELEASE_ID=$(curl -sf -H "Authorization: token ${TOKEN}" \
env: "${API_BASE}/releases/tags/release-candidate" \
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}' if [ -n "$RELEASE_ID" ]; then
run: | python3 -c "
# Ensure PHP + Composer are available import json, urllib.request
if ! command -v composer &> /dev/null; then body = open('/dev/stdin').read()
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 payload = json.dumps({'body': body}).encode()
fi req = urllib.request.Request(
# Always fetch latest CLI tools — never use stale cache from previous runs '${API_BASE}/releases/${RELEASE_ID}',
rm -rf /tmp/moko-platform-api data=payload, method='PATCH',
git clone --depth 1 --branch main --quiet \ headers={
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ 'Authorization': 'token ${TOKEN}',
/tmp/moko-platform-api 'Content-Type': 'application/json'
cd /tmp/moko-platform-api })
composer install --no-dev --no-interaction --quiet urllib.request.urlopen(req)
" <<< "$NOTES"
echo "RC release notes updated from CHANGELOG.md"
- name: "Publish stable release" fi
run: |
php /tmp/moko-platform-api/cli/release_publish.php \ >>>>>>> main
--path . --stability stable --bump minor --branch main \ - name: Summary
--token "${{ secrets.MOKOGITEA_TOKEN }}" \ if: always()
--skip-update-stream run: |
echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY
# -- STEP 9: Mirror to GitHub (stable only) -------------------------------- echo "Branch renamed to rc, minor bump, RC release built" >> $GITHUB_STEP_SUMMARY
- name: "Step 9: Mirror release to GitHub"
if: >- # ── Merged PR → Build & Release (or promote RC to stable) ────────────────────
steps.version.outputs.skip != 'true' && release:
secrets.GH_MIRROR_TOKEN != '' name: Build & Release Pipeline
continue-on-error: true runs-on: release
run: | if: >-
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" github.event.pull_request.merged == true ||
RELEASE_TAG="${{ steps.version.outputs.release_tag }}" (github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc')
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" steps:
php /tmp/moko-platform-api/cli/release_mirror.php \ - name: Checkout repository
--version "$VERSION" --tag "$RELEASE_TAG" \ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ with:
--gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \ token: ${{ secrets.MOKOGITEA_TOKEN }}
--branch main 2>&1 || true fetch-depth: 0
echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY
- name: Configure git for bot pushes
# -- STEP 10: Sync main branch to GitHub mirror ---------------------------- run: |
- name: "Step 10: Push main to GitHub mirror" git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
if: >- git config --local user.name "gitea-actions[bot]"
steps.version.outputs.skip != 'true' && git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
secrets.GH_MIRROR_TOKEN != ''
continue-on-error: true - name: Check for merge conflict markers
run: | run: |
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" 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)
GH_ORG=$(echo "$GH_REPO" | cut -d/ -f1) if [ -n "$CONFLICTS" ]; then
GH_NAME=$(echo "$GH_REPO" | cut -d/ -f2) echo "::error::Merge conflict markers found — aborting release"
git remote add github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" 2>/dev/null || \ echo "## Release Blocked: Conflict Markers" >> $GITHUB_STEP_SUMMARY
git remote set-url github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" echo '```' >> $GITHUB_STEP_SUMMARY
git fetch origin main --depth=1 echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY
git push github origin/main:refs/heads/main --force 2>/dev/null \ echo '```' >> $GITHUB_STEP_SUMMARY
&& echo "main branch pushed to GitHub mirror" \ exit 1
|| echo "WARNING: GitHub mirror push failed" fi
echo "No conflict markers found"
- name: "Step 11: Delete rc branch and recreate dev from main"
if: steps.version.outputs.skip != 'true' <<<<<<< HEAD
continue-on-error: true - name: Setup MokoCLI tools
run: | =======
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - name: Setup mokocli tools
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" >>>>>>> main
env:
# Delete rc branch (ephemeral — created by promote-rc) MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
"${API_BASE}/branches/rc" 2>/dev/null \ COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}'
&& echo "Deleted rc branch" || echo "rc branch not found" run: |
<<<<<<< HEAD
# Delete dev branch # Check both new (mokocli) and legacy (mokoplatform) install paths
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ if [ -f /opt/mokocli/cli/version_bump.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then
"${API_BASE}/branches/dev" 2>/dev/null && echo "Deleted dev branch" echo "Using pre-installed /opt/mokocli"
echo MOKO_CLI=/opt/mokocli/cli >> $GITHUB_ENV
# Recreate dev from main (now includes version bump + changelog promotion) elif [ -f /opt/mokoplatform/cli/version_bump.php ] && [ -f /opt/mokoplatform/vendor/autoload.php ]; then
curl -sf -X POST -H "Authorization: token ${TOKEN}" \ echo "Using pre-installed /opt/mokoplatform (legacy path)"
-H "Content-Type: application/json" \ echo MOKO_CLI=/opt/mokoplatform/cli >> $GITHUB_ENV
"${API_BASE}/branches" \ else
-d '{"new_branch_name":"dev","old_branch_name":"main"}' 2>/dev/null && echo "Recreated dev from main" echo "Falling back to fresh clone"
=======
echo "Pre-release branches cleaned, dev reset from main" >> $GITHUB_STEP_SUMMARY if [ -f /opt/mokocli/cli/version_bump.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then
echo Using pre-installed /opt/mokocli
- name: "Step 12: Create version branch from main" echo MOKO_CLI=/opt/mokocli/cli >> $GITHUB_ENV
if: steps.version.outputs.skip != 'true' else
continue-on-error: true echo Falling back to fresh clone
run: | >>>>>>> main
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" if ! command -v composer > /dev/null 2>&1; then
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" 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
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" fi
BRANCH_NAME="version/${VERSION}" rm -rf /tmp/mokocli
MAIN_SHA=$(git rev-parse HEAD) 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
# Delete old version branch if it exists (same version re-release) cd /tmp/mokocli
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" "${API_BASE}/branches/${BRANCH_NAME}" 2>/dev/null && echo "Deleted old ${BRANCH_NAME}" composer install --no-dev --no-interaction --quiet
echo MOKO_CLI=/tmp/mokocli/cli >> $GITHUB_ENV
# Create version/XX.YY.ZZ from main fi
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"
<<<<<<< HEAD
echo "Version branch created: ${BRANCH_NAME} (${MAIN_SHA})" >> $GITHUB_STEP_SUMMARY - name: "Publish stable release"
run: |
php ${MOKO_CLI}/release_publish.php \
--path . --stability stable --bump minor --branch main \
# -- Dolibarr post-release: Reset dev version ----------------------------- --token "${{ secrets.MOKOGITEA_TOKEN }}"
- name: "Post-release: Reset dev version"
if: steps.version.outputs.skip != 'true' - name: Update release notes from CHANGELOG.md
continue-on-error: true run: |
run: | API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
php /tmp/moko-platform-api/cli/version_reset_dev.php \ # Extract [Unreleased] section from changelog
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \ if [ -f "CHANGELOG.md" ]; then
--branch dev --path . 2>&1 || true NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md)
[ -z "$NOTES" ] && NOTES="Stable release"
# -- Summary -------------------------------------------------------------- else
- name: Pipeline Summary NOTES="Stable release"
if: always() fi
run: |
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" # Update release body via API
PLATFORM="${{ steps.platform.outputs.platform }}" RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
if [ "${{ steps.version.outputs.skip }}" = "true" ]; then "${API_BASE}/releases/tags/stable" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY
echo "No VERSION in README.md" >> $GITHUB_STEP_SUMMARY =======
elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then - name: "Detect platform"
echo "## Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY id: platform
else run: |
echo "" >> $GITHUB_STEP_SUMMARY php ${MOKO_CLI}/platform_detect.php --path . --github-output 2>/dev/null || true
echo "## Build & Release Complete (${PLATFORM})" >> $GITHUB_STEP_SUMMARY php ${MOKO_CLI}/manifest_read.php --path . --github-output 2>/dev/null || true
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Step | Result |" >> $GITHUB_STEP_SUMMARY - name: "Determine version bump level"
echo "|------|--------|" >> $GITHUB_STEP_SUMMARY id: bump
echo "| Platform | \`${PLATFORM}\` |" >> $GITHUB_STEP_SUMMARY run: |
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY # Fix/patch branches: version was already bumped by pre-release, just strip suffix
echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY # Feature/dev branches: bump minor for the new stable release
echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY HEAD_REF="${{ github.event.pull_request.head.ref || 'dev' }}"
echo "| Release | [View](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY case "$HEAD_REF" in
fi 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"
echo "tag=stable" >> "$GITHUB_OUTPUT"
echo "release_tag=stable" >> "$GITHUB_OUTPUT"
echo "branch=main" >> "$GITHUB_OUTPUT"
echo "Published version: ${VERSION}"
- name: Update release notes and promote changelog
run: |
API_BASE="${GITEA_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
>>>>>>> main
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={
<<<<<<< HEAD
'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}',
=======
'Authorization': 'token ${TOKEN}',
>>>>>>> main
'Content-Type': 'application/json'
})
urllib.request.urlopen(req)
" <<< "$NOTES"
echo "Release notes updated from CHANGELOG.md"
fi
<<<<<<< HEAD
=======
# 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
>>>>>>> main
# -- 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 ${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="${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 ${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](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY
fi
+3 -3
View File
@@ -4,10 +4,10 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: MokoPlatform.Universal # INGROUP: MokoStandards.Universal
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.mokogitea/workflows/branch-cleanup.yml # PATH: /.mokogitea/workflows/branch-cleanup.yml
# VERSION: 09.23.00 # VERSION: 01.00.00
# BRIEF: Delete feature branches after PR merge # BRIEF: Delete feature branches after PR merge
name: "Branch Cleanup" name: "Branch Cleanup"
+191
View File
@@ -0,0 +1,191 @@
# 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:
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
+81 -12
View File
@@ -4,18 +4,30 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.CI <<<<<<< HEAD
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # INGROUP: MokoCLI.CI
# PATH: /.gitea/workflows/ci-platform.yml # REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.mokogitea/workflows/ci-platform.yml
# VERSION: 09.23.00 # VERSION: 09.23.00
# BRIEF: moko-platform CI — the standards engine validates itself # BRIEF: MokoCLI CI — the standards engine validates itself
=======
# INGROUP: mokoplatform.CI
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
# PATH: /.mokogitea/workflows/ci-platform.yml
# VERSION: 09.23.00
# BRIEF: mokoplatform CI — the standards engine validates itself
>>>>>>> main
# #
# +========================================================================+ # +========================================================================+
# | MOKO-PLATFORM CI | # | MOKOCLI CI |
# +========================================================================+ # +========================================================================+
# | | # | |
# | This is NOT a generic CI workflow. This is the self-validation | # | This is NOT a generic CI workflow. This is the self-validation |
# | pipeline for the central moko-platform enterprise engine. | <<<<<<< HEAD
# | pipeline for the central MokoCLI enterprise engine. |
=======
# | pipeline for the central mokoplatform enterprise engine. |
>>>>>>> main
# | | # | |
# | It dogfoods every tool the platform ships to governed repos: | # | It dogfoods every tool the platform ships to governed repos: |
# | | # | |
@@ -29,7 +41,11 @@
# | | # | |
# +========================================================================+ # +========================================================================+
name: "Platform: moko-platform CI" <<<<<<< HEAD
name: "Platform: MokoCLI CI"
=======
name: "Platform: mokoplatform CI"
>>>>>>> main
on: on:
push: push:
@@ -41,7 +57,7 @@ on:
paths-ignore: paths-ignore:
- '**.md' - '**.md'
- 'wiki/**' - 'wiki/**'
- '.gitea/ISSUE_TEMPLATE/**' - '.mokogitea/ISSUE_TEMPLATE/**'
pull_request: pull_request:
branches: branches:
- main - main
@@ -104,7 +120,7 @@ jobs:
echo "::error file=${file}::PHP syntax error" echo "::error file=${file}::PHP syntax error"
ERRORS=$((ERRORS + 1)) ERRORS=$((ERRORS + 1))
fi fi
done < <(find lib/ validate/ automation/ cli/ src/ deploy/ -name "*.php" -print0 2>/dev/null) done < <(find lib/ validate/ automation/ cli/ source/ src/ deploy/ -name "*.php" -print0 2>/dev/null)
{ {
echo "### PHP Syntax" echo "### PHP Syntax"
@@ -270,7 +286,7 @@ jobs:
echo "::warning file=${file}::Missing SPDX header" echo "::warning file=${file}::Missing SPDX header"
MISSING=$((MISSING + 1)) MISSING=$((MISSING + 1))
fi fi
done < <(find lib/ validate/ cli/ src/ automation/ deploy/ -name "*.php" -print0 2>/dev/null) done < <(find lib/ validate/ cli/ source/ src/ automation/ deploy/ -name "*.php" -print0 2>/dev/null)
{ {
echo "### License Headers" echo "### License Headers"
@@ -289,7 +305,7 @@ jobs:
echo "::error file=${file}::Potential hardcoded secret detected" echo "::error file=${file}::Potential hardcoded secret detected"
FOUND=$((FOUND + 1)) FOUND=$((FOUND + 1))
fi fi
done < <(find lib/ validate/ cli/ src/ automation/ deploy/ -name "*.php" -print0 2>/dev/null) done < <(find lib/ validate/ cli/ source/ src/ automation/ deploy/ -name "*.php" -print0 2>/dev/null)
{ {
echo "### Secret Detection" echo "### Secret Detection"
@@ -412,10 +428,20 @@ jobs:
if: always() if: always()
steps: steps:
- name: Checkout
uses: actions/checkout@v4
with:
sparse-checkout: automation/ci-issue-reporter.sh
sparse-checkout-cone-mode: false
- name: Check gate results - name: Check gate results
run: | run: |
{ {
echo "# moko-platform CI" <<<<<<< HEAD
echo "# MokoCLI CI"
=======
echo "# mokoplatform CI"
>>>>>>> main
echo "" echo ""
echo "| Gate | Job | Status |" echo "| Gate | Job | Status |"
echo "|---|---|---|" echo "|---|---|---|"
@@ -437,3 +463,46 @@ jobs:
echo "::error::One or more CI gates failed" echo "::error::One or more CI gates failed"
exit 1 exit 1
fi fi
- name: "File issues for failed gates"
if: >-
always() &&
(needs.code-quality.result == 'failure' ||
needs.tests.result == 'failure' ||
needs.self-health.result == 'failure' ||
needs.governance.result == 'failure' ||
needs.templates.result == 'failure')
env:
GITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
run: |
chmod +x automation/ci-issue-reporter.sh
REPORTER="./automation/ci-issue-reporter.sh"
WF="Platform CI"
report_gate() {
local gate="$1" result="$2" details="$3"
if [ "$result" = "failure" ]; then
"$REPORTER" --gate "$gate" --details "$details" --workflow "$WF" --severity error
fi
}
report_gate "Code Quality" \
"${{ needs.code-quality.result }}" \
"PHPCS (PSR-12), PHPStan, or PHP syntax checks failed. Run \`composer check\` locally to reproduce."
report_gate "Unit Tests" \
"${{ needs.tests.result }}" \
"PHPUnit tests failed on one or more PHP versions (8.1, 8.2, 8.3). Run \`vendor/bin/phpunit --testdox\` locally."
report_gate "Self-Health" \
"${{ needs.self-health.result }}" \
"Self-health score fell below the 80% threshold. Run \`php bin/moko health -- --path .\` locally."
report_gate "Governance" \
"${{ needs.governance.result }}" \
"Governance checks failed (license headers, secrets, or version consistency). Check the CI run summary for specifics."
report_gate "Template Integrity" \
"${{ needs.templates.result }}" \
"Workflow or gitignore templates failed YAML validation or are missing required entries."
+17 -10
View File
@@ -4,10 +4,17 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Maintenance <<<<<<< HEAD
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # INGROUP: MokoCLI.Maintenance
# PATH: /.gitea/workflows/cleanup.yml # REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.mokogitea/workflows/cleanup.yml
# VERSION: 09.23.00 # VERSION: 09.23.00
=======
# INGROUP: MokoStandards.Maintenance
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
# PATH: /.gitea/workflows/cleanup.yml
# VERSION: 01.00.00
>>>>>>> main
# BRIEF: Scheduled cleanup — delete merged branches and old workflow runs # BRIEF: Scheduled cleanup — delete merged branches and old workflow runs
name: "Universal: Repository Cleanup" name: "Universal: Repository Cleanup"
@@ -33,17 +40,17 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
token: ${{ secrets.MOKOGITEA_TOKEN }} token: ${{ secrets.GA_TOKEN }}
- name: Delete merged branches - name: Delete merged branches
env: env:
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} GA_TOKEN: ${{ secrets.GA_TOKEN }}
run: | run: |
echo "=== Merged Branch Cleanup ===" echo "=== Merged Branch Cleanup ==="
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}" API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
# List branches via API # List branches via API
BRANCHES=$(curl -sS -H "Authorization: token ${GITEA_TOKEN}" \ BRANCHES=$(curl -sS -H "Authorization: token ${GA_TOKEN}" \
"${API}/branches?limit=50" | jq -r '.[].name') "${API}/branches?limit=50" | jq -r '.[].name')
DELETED=0 DELETED=0
@@ -56,7 +63,7 @@ jobs:
# Check if branch is merged into main # Check if branch is merged into main
if git merge-base --is-ancestor "origin/${BRANCH}" origin/main 2>/dev/null; then if git merge-base --is-ancestor "origin/${BRANCH}" origin/main 2>/dev/null; then
echo " Deleting merged branch: ${BRANCH}" echo " Deleting merged branch: ${BRANCH}"
curl -sS -X DELETE -H "Authorization: token ${GITEA_TOKEN}" \ curl -sS -X DELETE -H "Authorization: token ${GA_TOKEN}" \
"${API}/branches/${BRANCH}" 2>/dev/null || true "${API}/branches/${BRANCH}" 2>/dev/null || true
DELETED=$((DELETED + 1)) DELETED=$((DELETED + 1))
fi fi
@@ -66,20 +73,20 @@ jobs:
- name: Clean old workflow runs - name: Clean old workflow runs
env: env:
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} GA_TOKEN: ${{ secrets.GA_TOKEN }}
run: | run: |
echo "=== Workflow Run Cleanup ===" echo "=== Workflow Run Cleanup ==="
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}" API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
CUTOFF=$(date -d "30 days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -v-30d +%Y-%m-%dT%H:%M:%SZ) CUTOFF=$(date -d "30 days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -v-30d +%Y-%m-%dT%H:%M:%SZ)
# Get old completed runs # Get old completed runs
RUNS=$(curl -sS -H "Authorization: token ${GITEA_TOKEN}" \ RUNS=$(curl -sS -H "Authorization: token ${GA_TOKEN}" \
"${API}/actions/runs?status=completed&limit=50" | \ "${API}/actions/runs?status=completed&limit=50" | \
jq -r ".workflow_runs[] | select(.created_at < \"${CUTOFF}\") | .id" 2>/dev/null) jq -r ".workflow_runs[] | select(.created_at < \"${CUTOFF}\") | .id" 2>/dev/null)
DELETED=0 DELETED=0
for RUN_ID in $RUNS; do for RUN_ID in $RUNS; do
curl -sS -X DELETE -H "Authorization: token ${GITEA_TOKEN}" \ curl -sS -X DELETE -H "Authorization: token ${GA_TOKEN}" \
"${API}/actions/runs/${RUN_ID}" 2>/dev/null || true "${API}/actions/runs/${RUN_ID}" 2>/dev/null || true
DELETED=$((DELETED + 1)) DELETED=$((DELETED + 1))
done done
+126
View File
@@ -0,0 +1,126 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.Deploy
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards-API
# PATH: /templates/workflows/joomla/deploy-manual.yml.template
# VERSION: 04.07.00
# BRIEF: Manual SFTP deploy to dev server for Joomla repos
name: "Universal: Deploy to Dev (Manual)"
on:
workflow_dispatch:
inputs:
clear_remote:
description: 'Delete all remote files before uploading'
required: false
default: 'false'
type: boolean
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
permissions:
contents: read
jobs:
deploy:
name: SFTP Deploy to Dev
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Setup PHP
run: |
php -v && composer --version
- name: Setup MokoStandards tools
env:
GA_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }}
MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }}
MOKO_CLONE_HOST: ${{ secrets.GA_TOKEN && 'git.mokoconsulting.tech/MokoConsulting' || 'github.com/mokoconsulting-tech' }}
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GA_TOKEN || github.token }}"}}'
run: |
git clone --depth 1 --branch main --quiet \
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/MokoStandards-API.git" \
/tmp/mokostandards-api 2>/dev/null || true
if [ -d "/tmp/mokostandards-api" ] && [ -f "/tmp/mokostandards-api/composer.json" ]; then
cd /tmp/mokostandards-api && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
fi
- name: Check FTP configuration
id: check
env:
HOST: ${{ vars.DEV_FTP_HOST }}
PATH_VAR: ${{ vars.DEV_FTP_PATH }}
PORT: ${{ vars.DEV_FTP_PORT }}
run: |
if [ -z "$HOST" ] || [ -z "$PATH_VAR" ]; then
echo "DEV_FTP_HOST or DEV_FTP_PATH not configured -- cannot deploy"
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "skip=false" >> "$GITHUB_OUTPUT"
echo "host=$HOST" >> "$GITHUB_OUTPUT"
REMOTE="${PATH_VAR%/}"
echo "remote=$REMOTE" >> "$GITHUB_OUTPUT"
[ -z "$PORT" ] && PORT="22"
echo "port=$PORT" >> "$GITHUB_OUTPUT"
- name: Deploy via SFTP
if: steps.check.outputs.skip != 'true'
env:
SFTP_KEY: ${{ secrets.DEV_FTP_KEY }}
SFTP_PASS: ${{ secrets.DEV_FTP_PASSWORD }}
SFTP_USER: ${{ vars.DEV_FTP_USERNAME }}
run: |
SOURCE_DIR="src"
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
[ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/ -- nothing to deploy"; exit 0; }
printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \
"${{ steps.check.outputs.host }}" "${{ steps.check.outputs.port }}" "$SFTP_USER" "${{ steps.check.outputs.remote }}" \
> /tmp/sftp-config.json
if [ -n "$SFTP_KEY" ]; then
echo "$SFTP_KEY" > /tmp/deploy_key
chmod 600 /tmp/deploy_key
printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json
else
printf ',"password":"%s"}' "$SFTP_PASS" >> /tmp/sftp-config.json
fi
DEPLOY_ARGS=(--path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json)
[ "${{ inputs.clear_remote }}" = "true" ] && DEPLOY_ARGS+=(--clear-remote)
PLATFORM=$(php /tmp/mokostandards-api/cli/platform_detect.php --path . 2>/dev/null || true)
if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/mokostandards-api/deploy/deploy-joomla.php" ]; then
php /tmp/mokostandards-api/deploy/deploy-joomla.php "${DEPLOY_ARGS[@]}"
else
php /tmp/mokostandards-api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}"
fi
rm -f /tmp/deploy_key /tmp/sftp-config.json
- name: Summary
if: always()
run: |
if [ "${{ steps.check.outputs.skip }}" = "true" ]; then
echo "### Deploy Skipped -- FTP not configured" >> $GITHUB_STEP_SUMMARY
else
echo "### Manual Dev Deploy Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Host | \`${{ steps.check.outputs.host }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Remote | \`${{ steps.check.outputs.remote }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Clear | ${{ inputs.clear_remote }} |" >> $GITHUB_STEP_SUMMARY
fi
+8 -7
View File
@@ -4,10 +4,15 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Security <<<<<<< HEAD
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform # INGROUP: MokoCLI.Security
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
# INGROUP: MokoStandards.Security
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API
>>>>>>> main
# PATH: /templates/workflows/gitleaks.yml.template # PATH: /templates/workflows/gitleaks.yml.template
# VERSION: 09.23.00 # VERSION: 01.00.00
# BRIEF: Secret scanning — detect leaked credentials, API keys, and tokens # BRIEF: Secret scanning — detect leaked credentials, API keys, and tokens
# #
# +========================================================================+ # +========================================================================+
@@ -25,10 +30,6 @@
name: "Universal: Secret Scanning" name: "Universal: Secret Scanning"
on: on:
pull_request:
branches:
- main
- 'dev/**'
schedule: schedule:
- cron: '0 5 * * 1' # Weekly Monday 05:00 UTC - cron: '0 5 * * 1' # Weekly Monday 05:00 UTC
workflow_dispatch: workflow_dispatch:
+8 -3
View File
@@ -4,8 +4,13 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Automation <<<<<<< HEAD
# VERSION: 09.23.00 # INGROUP: MokoCLI.Automation
# VERSION: 09.25.05
=======
# INGROUP: mokocli.Automation
# VERSION: 09.32.00
>>>>>>> main
# BRIEF: Auto-create feature branch when an issue is opened # BRIEF: Auto-create feature branch when an issue is opened
name: "Universal: Issue Branch" name: "Universal: Issue Branch"
@@ -28,7 +33,7 @@ jobs:
steps: steps:
- name: Create branch and comment - name: Create branch and comment
run: | run: |
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" TOKEN="${{ secrets.GA_TOKEN }}"
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}" API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
ISSUE_NUM="${{ github.event.issue.number }}" ISSUE_NUM="${{ github.event.issue.number }}"
ISSUE_TITLE="${{ github.event.issue.title }}" ISSUE_TITLE="${{ github.event.issue.title }}"
+10 -3
View File
@@ -4,10 +4,17 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Notifications <<<<<<< HEAD
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # INGROUP: MokoCLI.Notifications
# PATH: /.gitea/workflows/notify.yml # REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.mokogitea/workflows/notify.yml
# VERSION: 09.23.00 # VERSION: 09.23.00
=======
# INGROUP: MokoStandards.Notifications
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
# PATH: /.gitea/workflows/notify.yml
# VERSION: 01.00.00
>>>>>>> main
# BRIEF: Push notifications via ntfy on release success or workflow failure # BRIEF: Push notifications via ntfy on release success or workflow failure
name: "Universal: Notifications" name: "Universal: Notifications"
+28 -2
View File
@@ -4,8 +4,8 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.CI # INGROUP: MokoCLI.CI
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform # REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /templates/workflows/universal/pr-check.yml.template # PATH: /templates/workflows/universal/pr-check.yml.template
# VERSION: 09.23.00 # VERSION: 09.23.00
# BRIEF: PR gate — branch policy + code validation before merge # BRIEF: PR gate — branch policy + code validation before merge
@@ -96,6 +96,32 @@ jobs:
echo "Branch policy: OK (${HEAD} → ${BASE})" echo "Branch policy: OK (${HEAD} → ${BASE})"
echo "## Branch Policy: Passed" >> $GITHUB_STEP_SUMMARY echo "## Branch Policy: Passed" >> $GITHUB_STEP_SUMMARY
# ── Secret Scanning ──────────────────────────────────────────────────
gitleaks:
name: Secret Scan
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Gitleaks
run: |
GITLEAKS_VERSION="8.21.2"
curl -sSL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \
| tar -xz -C /usr/local/bin gitleaks
- name: Scan PR commits for secrets
run: |
if gitleaks detect --source . --verbose \
--log-opts=${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} 2>&1; then
echo "**No secrets detected.**" >> $GITHUB_STEP_SUMMARY
else
echo "::error::Potential secrets detected in PR commits"
exit 1
fi
# ── Code Validation ──────────────────────────────────────────────────── # ── Code Validation ────────────────────────────────────────────────────
validate: validate:
name: Validate PR name: Validate PR
+121 -33
View File
@@ -4,19 +4,30 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Release <<<<<<< HEAD
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # INGROUP: MokoCLI.Release
=======
# INGROUP: mokocli.Release
>>>>>>> main
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /templates/workflows/universal/pre-release.yml.template # PATH: /templates/workflows/universal/pre-release.yml.template
# VERSION: 09.23.00 # VERSION: 05.01.00
# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch # BRIEF: Auto pre-release on push to dev/alpha/beta/rc branches
name: "Universal: Pre-Release" name: "Universal: Pre-Release"
on: on:
pull_request: push:
types: [closed]
branches: branches:
- dev - dev
- 'fix/**'
- 'patch/**'
- 'hotfix/**'
- 'bugfix/**'
- 'chore/**'
- alpha
- beta
- rc
workflow_dispatch: workflow_dispatch:
inputs: inputs:
stability: stability:
@@ -39,11 +50,11 @@ env:
jobs: jobs:
build: build:
name: "Build Pre-Release (${{ inputs.stability || 'development' }})" name: "Build Pre-Release (${{ inputs.stability || github.ref_name }})"
runs-on: release runs-on: release
if: >- if: >-
github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_dispatch' ||
(github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev') github.event_name == 'push'
steps: steps:
- name: Checkout - name: Checkout
@@ -51,56 +62,97 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
token: ${{ secrets.MOKOGITEA_TOKEN }} token: ${{ secrets.MOKOGITEA_TOKEN }}
ref: ${{ github.ref_name }}
- name: Setup moko-platform tools <<<<<<< HEAD
- name: Setup MokoCLI tools
=======
- name: Setup mokocli tools
>>>>>>> main
env: env:
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
run: | run: |
if ! command -v composer &> /dev/null; then <<<<<<< HEAD
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 # Check both new (mokocli) and legacy (mokoplatform) install paths
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
elif [ -f /opt/mokoplatform/cli/version_bump.php ] && [ -f /opt/mokoplatform/vendor/autoload.php ]; then
echo "Using pre-installed /opt/mokoplatform (legacy path)"
echo MOKO_CLI=/opt/mokoplatform/cli >> $GITHUB_ENV
else
echo "Falling back to fresh clone"
=======
# 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
>>>>>>> main
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 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 - name: Detect platform
id: platform id: platform
run: | 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 php ${MOKO_CLI}/manifest_read.php --path . --github-output
- name: Resolve metadata and bump version - name: Resolve metadata and bump version
id: meta id: meta
run: | run: |
STABILITY="${{ inputs.stability || 'development' }}" # 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
case "$STABILITY" in case "$STABILITY" in
development) TAG="development" ;; development) SUFFIX="-dev"; TAG="development" ;;
alpha) TAG="alpha" ;; alpha) SUFFIX="-alpha"; TAG="alpha" ;;
beta) TAG="beta" ;; beta) SUFFIX="-beta"; TAG="beta" ;;
release-candidate) TAG="release-candidate" ;; release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;;
esac esac
# Bump version: patch for dev/alpha/beta, minor for RC # Bump version via CLI: patch for dev/alpha/beta, minor for RC
case "$STABILITY" in case "$STABILITY" in
release-candidate) php ${MOKO_CLI}/version_bump.php --path . --minor 2>/dev/null || true ;; release-candidate) BUMP="minor" ;;
*) php ${MOKO_CLI}/version_bump.php --path . 2>/dev/null || true ;; *) BUMP="patch" ;;
esac esac
# Set stability suffix and fix consistency php ${MOKO_CLI}/version_bump.php --path . $([ "$BUMP" = "minor" ] && echo "--minor") 2>/dev/null || true
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo '00.00.01')
# 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\)$//') VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//')
php ${MOKO_CLI}/version_set_platform.php \ php ${MOKO_CLI}/version_set_platform.php \
--path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true --path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
# Read final version with suffix # Ensure licensing tags (updateservers, dlid) if enabled in manifest.xml
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null) php ${MOKO_CLI}/manifest_licensing.php --path . --fix 2>/dev/null || true
[ -z "$VERSION" ] && VERSION="00.00.01"
# Append suffix for output
if [ -n "$SUFFIX" ]; then
VERSION="${VERSION}${SUFFIX}"
fi
# Commit version bump # Commit version bump
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
@@ -125,11 +177,12 @@ jobs:
echo "version=${VERSION}" >> "$GITHUB_OUTPUT" echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT" echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT" echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT" echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT" echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT"
echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION} ===" echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ==="
- name: Create release - name: Create release
id: release id: release
@@ -140,7 +193,42 @@ jobs:
php ${MOKO_CLI}/release_create.php \ php ${MOKO_CLI}/release_create.php \
--path . --version "$VERSION" --tag "$TAG" \ --path . --version "$VERSION" --tag "$TAG" \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
--repo "${GITEA_REPO}" --branch dev --prerelease --repo "${GITEA_REPO}" --branch "${{ github.ref_name }}" --prerelease
- name: Update release notes from CHANGELOG.md
run: |
TAG="${{ steps.meta.outputs.tag }}"
VERSION="${{ steps.meta.outputs.version }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
# 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
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 - name: Build package and upload
id: package id: package
+66
View File
@@ -0,0 +1,66 @@
# 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
run: |
BRANCH="${{ github.event.pull_request.head.ref }}"
SUFFIX="${BRANCH#rc/}"
DEV_BRANCH="dev/${SUFFIX}"
API="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}/api/v1/repos/${{ github.repository }}/branches"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
# 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
# Delete rc/ branch
ENCODED=$(php -r "echo rawurlencode('${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
+10 -19
View File
@@ -4,10 +4,17 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Security <<<<<<< HEAD
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # INGROUP: MokoCLI.Security
# PATH: /.gitea/workflows/security-audit.yml # REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.mokogitea/workflows/security-audit.yml
# VERSION: 09.23.00 # VERSION: 09.23.00
=======
# INGROUP: MokoStandards.Security
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
# PATH: /.gitea/workflows/security-audit.yml
# VERSION: 01.00.00
>>>>>>> main
# BRIEF: Dependency vulnerability scanning for composer and npm packages # BRIEF: Dependency vulnerability scanning for composer and npm packages
name: "Universal: Security Audit" name: "Universal: Security Audit"
@@ -80,19 +87,3 @@ jobs:
-H "Priority: high" \ -H "Priority: high" \
-d "Security audit found vulnerabilities. Review dependency updates." \ -d "Security audit found vulnerabilities. Review dependency updates." \
"${NTFY_URL}/${NTFY_TOPIC}" || true "${NTFY_URL}/${NTFY_TOPIC}" || true
- name: Joomla version audit
if: always()
run: |
if [ -f "monitoring/joomla-version-audit.php" ] && [ -n "$JOOMLA_SITES" ]; then
echo "$JOOMLA_SITES" > /tmp/sites.json
php monitoring/joomla-version-audit.php --sites /tmp/sites.json || true
echo "### Joomla Version Audit" >> $GITHUB_STEP_SUMMARY
rm -f /tmp/sites.json
else
echo "Joomla audit skipped (no script or JOOMLA_SITES_JSON not configured)"
fi
env:
JOOMLA_SITES: ${{ vars.JOOMLA_SITES_JSON }}
@@ -0,0 +1,103 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Automation
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.mokogitea/workflows/sync-feature-versions.yml
# VERSION: 01.00.00
# BRIEF: Merge dev into open feature branches after version bumps
name: "Universal: Sync Feature Branch Versions"
on:
push:
branches:
- dev
workflow_dispatch:
permissions:
contents: write
env:
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
jobs:
sync:
name: Sync feature branches with dev
runs-on: ubuntu-latest
if: >-
github.event_name == 'workflow_dispatch' ||
contains(github.event.head_commit.message, 'chore(version)')
steps:
- name: Checkout dev
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: dev
token: ${{ secrets.MOKOGITEA_TOKEN }}
- name: Configure git
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: Merge dev into feature branches
run: |
echo "=== Syncing feature branches with dev ==="
# Fetch all remote branches
git fetch origin
# Find feature branches (feature/*, fix/*, patch/*, hotfix/*, bugfix/*, chore/*)
BRANCHES=$(git branch -r --list 'origin/feature/*' 'origin/fix/*' 'origin/patch/*' 'origin/hotfix/*' 'origin/bugfix/*' 'origin/chore/*' | sed 's|origin/||; s/^[[:space:]]*//')
if [ -z "$BRANCHES" ]; then
echo "No feature branches found — nothing to sync"
exit 0
fi
SYNCED=0
SKIPPED=0
FAILED=0
for BRANCH in $BRANCHES; do
echo ""
echo "--- ${BRANCH} ---"
# Skip branches that are already up to date with dev
if git merge-base --is-ancestor dev "origin/${BRANCH}" 2>/dev/null; then
echo "Already up to date"
SKIPPED=$((SKIPPED + 1))
continue
fi
# Try to merge dev into the branch
git checkout "origin/${BRANCH}" -B "$BRANCH" 2>/dev/null
if git merge dev --no-edit -m "chore: merge dev into ${BRANCH} (version sync) [skip ci]" 2>/dev/null; then
git push origin "$BRANCH" 2>/dev/null
echo "Synced successfully"
SYNCED=$((SYNCED + 1))
else
git merge --abort 2>/dev/null || true
echo "Merge conflict — skipping (manual rebase needed)"
FAILED=$((FAILED + 1))
fi
done
# Return to dev
git checkout dev 2>/dev/null || true
echo ""
echo "=== Summary ==="
echo "Synced: ${SYNCED}"
echo "Already current: ${SKIPPED}"
echo "Conflicts (skipped): ${FAILED}"
if [ "$FAILED" -gt 0 ]; then
echo "::warning::${FAILED} branch(es) had merge conflicts and need manual rebase"
fi
-302
View File
@@ -1,302 +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: 09.23.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"
# 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
# Gitea release tag per stability
case "$STABILITY" in
development) TAG="development" ;;
alpha) TAG="alpha" ;;
beta) TAG="beta" ;;
rc) TAG="release-candidate" ;;
*) TAG="stable" ;;
esac
# Bump patch, set platform suffix, fix consistency — version_bump preserves suffix
php ${MOKO_CLI}/version_set_platform.php \
--path . --version "$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo '00.00.01')" \
--branch "$BRANCH" --stability "$STABILITY" 2>/dev/null || true
php ${MOKO_CLI}/version_bump.php --path . 2>/dev/null || true
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
# Read final version (includes suffix, e.g. 01.02.15-dev)
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "00.00.01")
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
echo "tag=${TAG}" >> "$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="${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
@@ -0,0 +1,73 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Universal
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.mokogitea/workflows/workflow-sync-trigger.yml
# VERSION: 01.01.00
# BRIEF: Trigger workflow sync to live repos when a PR is merged to main
name: "Universal: Workflow Sync Trigger"
on:
pull_request:
types: [closed]
branches:
- main
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
sync:
name: Sync workflows to live repos
runs-on: ubuntu-latest
if: >-
github.event.pull_request.merged == true &&
!contains(github.event.pull_request.title, '[skip sync]')
steps:
- name: Determine platform from repo name
id: platform
run: |
REPO="${{ github.event.repository.name }}"
case "$REPO" in
Template-Joomla) PLATFORM="joomla" ;;
Template-Dolibarr) PLATFORM="dolibarr" ;;
Template-Go) PLATFORM="go" ;;
Template-MCP) PLATFORM="mcp" ;;
Template-Generic) PLATFORM="" ;;
*) PLATFORM="" ;;
esac
echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT"
echo "Platform: ${PLATFORM:-all}"
- name: Clone mokocli
env:
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
run: |
GITEA_URL="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}"
git clone --depth 1 "${GITEA_URL}/MokoConsulting/mokocli.git" /tmp/mokocli
- name: Install dependencies
run: |
cd /tmp/mokocli
composer install --no-dev --no-interaction --quiet 2>/dev/null || true
- name: Run workflow sync
env:
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
run: |
ARGS="--token ${MOKOGITEA_TOKEN}"
ARGS="${ARGS} --org ${{ vars.GITEA_ORG || github.repository_owner }}"
ARGS="${ARGS} --phase repos"
PLATFORM="${{ steps.platform.outputs.platform }}"
if [ -n "$PLATFORM" ]; then
ARGS="${ARGS} --platform-filter ${PLATFORM}"
fi
php /tmp/mokocli/cli/workflow_sync.php ${ARGS}
+5 -1
View File
@@ -1,7 +1,11 @@
{ {
"metadata": { "metadata": {
"generated_at": "2026-03-10T19:51:42.238134Z", "generated_at": "2026-03-10T19:51:42.238134Z",
"repository": "MokoConsulting/moko-platform", <<<<<<< HEAD
"repository": "MokoConsulting/mokocli",
=======
"repository": "MokoConsulting/mokoplatform",
>>>>>>> main
"version": "1.0.0" "version": "1.0.0"
}, },
"scripts": [ "scripts": [
+33 -22
View File
@@ -2,37 +2,48 @@
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech> Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
SPDX-License-Identifier: GPL-3.0-or-later SPDX-License-Identifier: GPL-3.0-or-later
FILE INFORMATION FILE INFORMATION
<<<<<<< HEAD
DEFGROUP: MokoStandards.Root DEFGROUP: MokoStandards.Root
INGROUP: MokoStandards INGROUP: MokoStandards
REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
DEFGROUP: MokoCli.Root
INGROUP: MokoCli
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
PATH: /CHANGELOG.md PATH: /CHANGELOG.md
BRIEF: Release changelog BRIEF: Release changelog
--> -->
# Changelog # Changelog
## [Unreleased] ## [Unreleased]
## [09.23] --- 2026-05-31 ## [09.32.00] --- 2026-06-21
## [09.22] --- 2026-05-31 ## [09.32.00] --- 2026-06-21
## [09.31.00] --- 2026-06-21
## [09.31.00] --- 2026-06-21
## [09.30.00] --- 2026-06-21
## [09.30.00] --- 2026-06-21
### Added
- `security:advisories` command — cross-repo security advisory aggregator (#150)
- Scans org repos for known CVEs via `composer audit`
- Aggregates results into a single report with severity breakdown
- Auto-creates tracking issues for critical/high vulnerabilities (`--create-issues`)
- Checkpoint-based resumability with `--resume`
- Export to JSON/CSV with `--export`
### Changed ### Changed
- **refactor(cli):** migrate 64 legacy scripts to CliFramework (#235) — all tools in cli/, automation/, maintenance/, deploy/, release/ now extend CliFramework with free --help, --verbose, --quiet, --dry-run, --json, banners, and coloured logging - `manifest:read` rewritten to use Gitea manifest API as primary source (#283)
- Falls back to auto-detection from source tree (Joomla, Dolibarr, generic)
### Fixed - No longer requires `.mokogitea/manifest.xml` file
- fix: auto-detect org/repo in updates_xml_build from manifest and git remote - Backward-compatible field aliases for existing CI consumers
- fix: restore hyphen in version suffixes - Renamed `MokoStandards` namespace → `MokoCli` across all files
- fix: release names use standardized format - Renamed `MokoEnterprise` namespace → `MokoCli` across all files
- fix: remove lesser stream copies, each stream updates independently - Renamed `MokoStandardsParser` class → `ManifestParser`
- fix: sort updates.xml entries dev first, stable last - Fixed `composer.json` autoload paths: `src/``source/`
## [09.21] --- 2026-05-30
## [09.20] --- 2026-05-30
## [09.19] --- 2026-05-30
## [09.18] --- 2026-05-30
## [09.17] --- 2026-05-30
-102
View File
@@ -1,102 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code when working with this repository.
## Project Overview
**moko-platform** — Enterprise automation, validation, sync, and governance engine for all Moko Consulting repositories
| Field | Value |
|---|---|
| **Language** | PHP 8.1+ |
| **Default branch** | main |
| **License** | GPL-3.0-or-later |
| **Version** | 09.01.00 |
| **Wiki** | [moko-platform Wiki](https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki) |
## Common Commands
```bash
composer install # Install PHP dependencies
php bin/moko health --path . # Run repo health check
php bin/moko check:syntax --path . # PHP syntax check
php bin/moko drift --org MokoConsulting # Scan for standards drift
php bin/moko dashboard --token $TOKEN -o dashboard.html # Generate client dashboard
# Code quality
php vendor/bin/phpcs --standard=phpcs.xml -n lib/ validate/ automation/ cli/
php vendor/bin/phpcbf --standard=phpcs.xml lib/ validate/ automation/ cli/
php vendor/bin/phpstan analyse -c phpstan.neon --memory-limit=512M
# Run all checks
composer check
```
## Architecture
### Directory Layout
| Directory | Purpose |
|-----------|---------|
| `cli/` | 32 standalone CLI tools (version, release, build, repo management) |
| `validate/` | 20 validation scripts (syntax, structure, manifests, drift) |
| `automation/` | 7 bulk operations (sync, push files, templates, cleanup) |
| `lib/Enterprise/` | Core library — CliFramework, ApiClient, adapters, validators |
| `lib/Enterprise/Plugins/` | 11 platform plugins (Joomla, Dolibarr, Node.js, Python, etc.) |
| `deploy/` | SFTP deployment scripts (Joomla, Dolibarr, health checks) |
| `templates/` | Universal templates, configs, governance schema |
| `.mokogitea/workflows/` | CI/CD workflows (Gitea Actions) |
| `bin/moko` | Unified CLI dispatcher — runs any tool via `php bin/moko <command>` |
### CLI Framework
All CLI tools extend `MokoEnterprise\CliFramework` (defined in `lib/Enterprise/CliFramework.php`).
Pattern for new tools:
```php
class MyTool extends CliFramework {
protected function configure(): void {
$this->setDescription('What this tool does');
$this->addArgument('--name', 'Description', 'default');
}
protected function run(): int {
$name = $this->getArgument('--name');
// ... business logic ...
return 0;
}
}
$app = new MyTool();
exit($app->execute());
```
Built-in flags: `--help`, `--verbose`, `--quiet`, `--dry-run`
### Platform Adapters
Git operations are abstracted via `GitPlatformAdapter` interface:
- `MokoGiteaAdapter` — for git.mokoconsulting.tech (primary)
- `GitHubAdapter` — for github.com mirrors
### Plugin System
Platform-specific logic lives in `lib/Enterprise/Plugins/`. Each plugin implements `ProjectPluginInterface` with methods for health checks, validation, build commands, and config schemas.
## Code Quality
| Tool | Level | Config |
|------|-------|--------|
| PHPCS | PSR-12 (errors only) | `phpcs.xml` |
| PHPStan | Level 2 | `phpstan.neon` |
PHPStan runs with `--memory-limit=512M` due to large codebase. CI enforces PHPCS errors; PHPStan is advisory (`continue-on-error`).
## Rules
- **Workflow directory**: `.mokogitea/` (not `.gitea/` or `.github/`)
- **Never commit** `.claude/`, `.mcp.json`, `TODO.md`, or `*.min.css`/`*.min.js`
- **Attribution**: use `Authored-by: Moko Consulting` in commits
- **Branch strategy**: develop on `dev`, merge to `main` for release
- **Minification**: handled at build time (CI) and runtime (MokoMinifyHelper for Joomla templates)
- **Wiki**: documentation lives in the Gitea wiki, not in `docs/` files
- **New CLI tools**: extend `CliFramework`, not `CLIApp` (legacy)
- **After adding a CLI tool**: register it in `bin/moko` COMMAND_MAP
+161 -161
View File
@@ -1,161 +1,161 @@
# Contributing to Moko Consulting Projects # Contributing to Moko Consulting Projects
Thank you for your interest in contributing. All Moko Consulting repositories follow this universal workflow and version policy. Thank you for your interest in contributing. All Moko Consulting repositories follow this universal workflow and version policy.
## Branching Workflow ## Branching Workflow
``` ```
feature/* ──PR──> dev ──draft PR──> (renamed to rc) ──merge──> main feature/* ──PR──> dev ──draft PR──> (renamed to rc) ──merge──> main
``` ```
### Step by step ### Step by step
1. **Create a feature branch** from `dev`: 1. **Create a feature branch** from `dev`:
```bash ```bash
git checkout dev && git pull git checkout dev && git pull
git checkout -b feature/my-change git checkout -b feature/my-change
``` ```
2. **Work and commit** on your feature branch. Push to origin. 2. **Work and commit** on your feature branch. Push to origin.
3. **Open a PR**: `feature/my-change` → `dev`. After review and checks, merge it. 3. **Open a PR**: `feature/my-change` → `dev`. After review and checks, merge it.
4. **When ready for release**, open a **draft PR**: `dev` → `main`. 4. **When ready for release**, open a **draft PR**: `dev` → `main`.
- This automatically renames the source branch to `rc` (release candidate) - This automatically renames the source branch to `rc` (release candidate)
- An RC pre-release is built and uploaded - An RC pre-release is built and uploaded
5. **Alpha and beta branches** are created by manually renaming the branch before the RC stage: 5. **Alpha and beta branches** are created by manually renaming the branch before the RC stage:
- Rename `dev` to `alpha` for early testing → alpha pre-release is built - Rename `dev` to `alpha` for early testing → alpha pre-release is built
- Rename `alpha` to `beta` for feature-complete testing → beta pre-release is built - Rename `alpha` to `beta` for feature-complete testing → beta pre-release is built
- When the draft PR is created, the branch is renamed to `rc` - When the draft PR is created, the branch is renamed to `rc`
6. **Once PR checks pass** on the `rc` branch, mark the PR as ready and merge to `main`. 6. **Once PR checks pass** on the `rc` branch, mark the PR as ready and merge to `main`.
7. **Merging to main** triggers the stable release pipeline: 7. **Merging to main** triggers the stable release pipeline:
- Minor version bump (e.g., `02.09.xx` → `02.10.00`) - Minor version bump (e.g., `02.09.xx` → `02.10.00`)
- Stability suffix stripped (clean version) - Stability suffix stripped (clean version)
- Gitea release created with ZIP/tar.gz packages - Gitea release created with ZIP/tar.gz packages
- `updates.xml` updated (Joomla extensions) - `updates.xml` updated (Joomla extensions)
- `dev` branch recreated from `main` - `dev` branch recreated from `main`
### Branch summary ### Branch summary
| Branch | Purpose | Created by | | Branch | Purpose | Created by |
|--------|---------|-----------| |--------|---------|-----------|
| `feature/*` | New features and fixes | Developer | | `feature/*` | New features and fixes | Developer |
| `dev` | Integration branch | Auto-recreated after release | | `dev` | Integration branch | Auto-recreated after release |
| `alpha` | Alpha pre-release testing | Manual rename from `dev` | | `alpha` | Alpha pre-release testing | Manual rename from `dev` |
| `beta` | Beta pre-release testing | Manual rename from `alpha` | | `beta` | Beta pre-release testing | Manual rename from `alpha` |
| `rc` | Release candidate | Auto-renamed on draft PR to main | | `rc` | Release candidate | Auto-renamed on draft PR to main |
| `main` | Stable releases | Protected, merge only | | `main` | Stable releases | Protected, merge only |
| `version/XX.YY.ZZ` | Archived release snapshots | Auto-created by CI | | `version/XX.YY.ZZ` | Archived release snapshots | Auto-created by CI |
### Protected branches ### Protected branches
| Branch | Direct push | Merge via | | Branch | Direct push | Merge via |
|--------|------------|-----------| |--------|------------|-----------|
| `main` | Blocked (CI bot whitelisted) | PR merge only | | `main` | Blocked (CI bot whitelisted) | PR merge only |
| `dev` | Blocked (CI bot whitelisted) | PR merge from feature/* | | `dev` | Blocked (CI bot whitelisted) | PR merge from feature/* |
| `rc` | Blocked (CI bot whitelisted) | Auto-created on draft PR | | `rc` | Blocked (CI bot whitelisted) | Auto-created on draft PR |
| `alpha` | Blocked (CI bot whitelisted) | Manual rename | | `alpha` | Blocked (CI bot whitelisted) | Manual rename |
| `beta` | Blocked (CI bot whitelisted) | Manual rename | | `beta` | Blocked (CI bot whitelisted) | Manual rename |
| `feature/*` | Open | N/A (source branch) | | `feature/*` | Open | N/A (source branch) |
## Version Policy ## Version Policy
### Format ### Format
All versions use `XX.YY.ZZ` — three two-digit segments, zero-padded: All versions use `XX.YY.ZZ` — three two-digit segments, zero-padded:
- **XX** — Major version (breaking changes) - **XX** — Major version (breaking changes)
- **YY** — Minor version (new features, bumped on release to main) - **YY** — Minor version (new features, bumped on release to main)
- **ZZ** — Patch version (auto-incremented on every push to dev/feature branches) - **ZZ** — Patch version (auto-incremented on every push to dev/feature branches)
Rollover: patch `99` → `00` increments minor; minor `99` → `00` increments major. Rollover: patch `99` → `00` increments minor; minor `99` → `00` increments major.
### Stability suffixes ### Stability suffixes
Each branch appends a suffix to indicate stability: Each branch appends a suffix to indicate stability:
| Branch | Suffix | Example | | Branch | Suffix | Example |
|--------|--------|---------| |--------|--------|---------|
| `main` | (none) | `02.09.00` | | `main` | (none) | `02.09.00` |
| `dev` | `-dev` | `02.09.01-dev` | | `dev` | `-dev` | `02.09.01-dev` |
| `feature/*` | `-dev` | `02.09.01-dev` | | `feature/*` | `-dev` | `02.09.01-dev` |
| `alpha` | `-alpha` | `02.09.01-alpha` | | `alpha` | `-alpha` | `02.09.01-alpha` |
| `beta` | `-beta` | `02.09.01-beta` | | `beta` | `-beta` | `02.09.01-beta` |
| `rc` | `-rc` | `02.09.01-rc` | | `rc` | `-rc` | `02.09.01-rc` |
### Auto version bump ### Auto version bump
On every push to `dev`, `feature/*`, or `patch/*`: On every push to `dev`, `feature/*`, or `patch/*`:
1. Patch version incremented 1. Patch version incremented
2. Stability suffix `-dev` applied 2. Stability suffix `-dev` applied
3. All version-bearing files updated (manifests, CHANGELOG, PHP headers, etc.) 3. All version-bearing files updated (manifests, CHANGELOG, PHP headers, etc.)
4. Commit created with `[skip ci]` to avoid loops 4. Commit created with `[skip ci]` to avoid loops
### Release version flow ### Release version flow
Version bumps happen at specific release events: Version bumps happen at specific release events:
| Event | Bump | Example | | Event | Bump | Example |
|-------|------|---------| |-------|------|---------|
| Feature merged to dev | Patch bump after dev release | `02.09.01-dev` → release → `02.09.02-dev` | | Feature merged to dev | Patch bump after dev release | `02.09.01-dev` → release → `02.09.02-dev` |
| Dev promoted to RC | Minor bump | `02.09.02-dev` → `02.10.00-rc` | | Dev promoted to RC | Minor bump | `02.09.02-dev` → `02.10.00-rc` |
| RC merged to main | Minor bump | `02.10.00-rc` → `02.11.00` (stable) | | RC merged to main | Minor bump | `02.10.00-rc` → `02.11.00` (stable) |
| Dev recreated from main | Patch bump | `02.11.00` → `02.11.01-dev` | | Dev recreated from main | Patch bump | `02.11.00` → `02.11.01-dev` |
### Release stream copies ### Release stream copies
When a higher-stability release is published, copies are created for all lesser streams with the same base version: When a higher-stability release is published, copies are created for all lesser streams with the same base version:
- **RC `02.10.00-rc`** also creates: `02.10.00-dev`, `02.10.00-alpha`, `02.10.00-beta` - **RC `02.10.00-rc`** also creates: `02.10.00-dev`, `02.10.00-alpha`, `02.10.00-beta`
- **Stable `02.11.00`** also creates: `02.11.00-dev`, `02.11.00-alpha`, `02.11.00-beta`, `02.11.00-rc` - **Stable `02.11.00`** also creates: `02.11.00-dev`, `02.11.00-alpha`, `02.11.00-beta`, `02.11.00-rc`
This ensures Joomla sites on ANY stability channel see the update (Joomla only shows versions higher than what's installed). This ensures Joomla sites on ANY stability channel see the update (Joomla only shows versions higher than what's installed).
### Version files ### Version files
The version tools update all files containing version stamps: The version tools update all files containing version stamps:
- `.mokogitea/manifest.xml` (canonical source) - `.mokogitea/manifest.xml` (canonical source)
- Joomla XML manifests (`<version>` tag) - Joomla XML manifests (`<version>` tag)
- `README.md`, `CHANGELOG.md` (`VERSION:` pattern) - `README.md`, `CHANGELOG.md` (`VERSION:` pattern)
- `package.json`, `pyproject.toml` - `package.json`, `pyproject.toml`
- Any text file with a `VERSION: XX.YY.ZZ` label - Any text file with a `VERSION: XX.YY.ZZ` label
Files synced from other repos (with a `# REPO:` header) are not touched. Files synced from other repos (with a `# REPO:` header) are not touched.
## Code Standards ## Code Standards
- **PHP**: PSR-12, tabs for indentation - **PHP**: PSR-12, tabs for indentation
- **Copyright**: all files must include the Moko Consulting copyright header - **Copyright**: all files must include the Moko Consulting copyright header
- **License**: SPDX identifier `GPL-3.0-or-later` (or as specified per repo) - **License**: SPDX identifier `GPL-3.0-or-later` (or as specified per repo)
- **Attribution**: use `Authored-by: Moko Consulting` in commits, not individual names - **Attribution**: use `Authored-by: Moko Consulting` in commits, not individual names
## Commit Messages ## Commit Messages
Use conventional commit format: Use conventional commit format:
``` ```
type(scope): short description type(scope): short description
Optional body with context. Optional body with context.
Authored-by: Moko Consulting Authored-by: Moko Consulting
``` ```
Types: `feat`, `fix`, `chore`, `docs`, `style`, `refactor`, `test`, `ci` Types: `feat`, `fix`, `chore`, `docs`, `style`, `refactor`, `test`, `ci`
Special flags in commit messages: Special flags in commit messages:
- `[skip ci]` — skip all CI workflows - `[skip ci]` — skip all CI workflows
- `[skip bump]` — skip auto version bump only - `[skip bump]` — skip auto version bump only
## Reporting Issues ## Reporting Issues
Use the repository's issue tracker with the appropriate template. Use the repository's issue tracker with the appropriate template.
--- ---
*Moko Consulting <hello@mokoconsulting.tech>* *Moko Consulting <hello@mokoconsulting.tech>*
+12 -2
View File
@@ -2,16 +2,26 @@
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech> Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
SPDX-License-Identifier: GPL-3.0-or-later SPDX-License-Identifier: GPL-3.0-or-later
FILE INFORMATION FILE INFORMATION
<<<<<<< HEAD
DEFGROUP: MokoCLI.Root
INGROUP: MokoCLI
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
DEFGROUP: MokoPlatform.Root DEFGROUP: MokoPlatform.Root
INGROUP: MokoPlatform INGROUP: MokoPlatform
REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
PATH: /PLUGIN_SCRIPTS.md PATH: /PLUGIN_SCRIPTS.md
BRIEF: Plugin system CLI documentation BRIEF: Plugin system CLI documentation
--> -->
# Plugin System CLI Scripts # Plugin System CLI Scripts
Command-line scripts for validating, health checking, and managing projects using the moko-platform plugin system. <<<<<<< HEAD
Command-line scripts for validating, health checking, and managing projects using the MokoCLI plugin system.
=======
Command-line scripts for validating, health checking, and managing projects using the mokoplatform plugin system.
>>>>>>> main
## Available Scripts ## Available Scripts
+24 -8
View File
@@ -2,22 +2,38 @@
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech> Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
SPDX-License-Identifier: GPL-3.0-or-later SPDX-License-Identifier: GPL-3.0-or-later
FILE INFORMATION FILE INFORMATION
DEFGROUP: MokoPlatform.Root <<<<<<< HEAD
INGROUP: MokoPlatform DEFGROUP: MokoCLI.Root
REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform INGROUP: MokoCLI
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
PATH: /README.md PATH: /README.md
VERSION: 09.23.00 VERSION: 09.25.05
BRIEF: Project overview and documentation BRIEF: Project overview and documentation
--> -->
# moko-platform Enterprise API # MokoCLI Enterprise API
![Version](https://img.shields.io/badge/version-09.01.00-blue) ![PHP](https://img.shields.io/badge/PHP-8.1%2B-777BB4) ![License](https://img.shields.io/badge/license-GPL--3.0--or--later-green) ![Version](https://img.shields.io/badge/version-09.01.00-blue) ![PHP](https://img.shields.io/badge/PHP-8.1%2B-777BB4) ![License](https://img.shields.io/badge/license-GPL--3.0--or--later-green)
PHP implementation of moko-platform — enterprise standards, automation framework, workflow templates, and bulk sync tooling. PHP implementation of MokoCLI — enterprise standards, automation framework, workflow templates, and bulk sync tooling.
=======
DEFGROUP: MokoPlatform.Root
INGROUP: MokoPlatform
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
PATH: /README.md
VERSION: 09.32.00
BRIEF: Project overview and documentation
-->
> **Primary platform**: [Gitea — git.mokoconsulting.tech](https://git.mokoconsulting.tech/MokoConsulting/MokoStandards-API) # mokoplatform Enterprise API
> **Backup mirror**: [GitHub](https://github.com/MokoConsulting/MokoStandards-API) *(read-only mirror)*
![Version](https://img.shields.io/badge/version-09.01.00-blue) ![PHP](https://img.shields.io/badge/PHP-8.1%2B-777BB4) ![License](https://img.shields.io/badge/license-GPL--3.0--or--later-green)
PHP implementation of mokoplatform — enterprise standards, automation framework, workflow templates, and bulk sync tooling.
>>>>>>> main
> **Primary platform**: [Gitea — git.mokoconsulting.tech](https://git.mokoconsulting.tech/MokoConsulting/MokoCli-API)
> **Backup mirror**: [GitHub](https://github.com/MokoConsulting/MokoCli-API) *(read-only mirror)*
## What Lives Here ## What Lives Here
+7 -1
View File
@@ -2,9 +2,15 @@
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech> Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
SPDX-License-Identifier: GPL-3.0-or-later SPDX-License-Identifier: GPL-3.0-or-later
FILE INFORMATION FILE INFORMATION
<<<<<<< HEAD
DEFGROUP: MokoCLI.Index
INGROUP: MokoCLI.Analysis
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
DEFGROUP: MokoPlatform.Index DEFGROUP: MokoPlatform.Index
INGROUP: MokoPlatform.Analysis INGROUP: MokoPlatform.Analysis
REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
PATH: /analysis/index.md PATH: /analysis/index.md
BRIEF: Analysis directory index BRIEF: Analysis directory index
--> -->
+18 -4
View File
@@ -9,9 +9,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
<<<<<<< HEAD
* DEFGROUP: MokoCLI.Automation
* INGROUP: MokoCLI.Scripts
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: MokoPlatform.Automation * DEFGROUP: MokoPlatform.Automation
* INGROUP: MokoPlatform.Scripts * INGROUP: MokoPlatform.Scripts
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /automation/bulk_joomla_template.php * PATH: /automation/bulk_joomla_template.php
* BRIEF: Bulk scaffold and sync Joomla template repositories * BRIEF: Bulk scaffold and sync Joomla template repositories
* *
@@ -28,7 +34,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\{ use MokoCli\{
AuditLogger, AuditLogger,
CliFramework, CliFramework,
Config, Config,
@@ -42,7 +48,11 @@ use MokoEnterprise\{
* *
* Provides three operations for Joomla template projects: * Provides three operations for Joomla template projects:
* --scaffold: Create a new template repository with the full directory structure * --scaffold: Create a new template repository with the full directory structure
* --sync: Push moko-platform files to existing template repositories <<<<<<< HEAD
* --sync: Push MokoCLI files to existing template repositories
=======
* --sync: Push mokoplatform files to existing template repositories
>>>>>>> main
* --list: List all repositories tagged as joomla-template * --list: List all repositories tagged as joomla-template
* *
* Works with both GitHub and Gitea via the PlatformAdapterFactory. * Works with both GitHub and Gitea via the PlatformAdapterFactory.
@@ -318,7 +328,11 @@ class BulkJoomlaTemplate extends CliFramework
$name, $name,
$path, $path,
$content, $content,
"chore: update {$path} from moko-platform", <<<<<<< HEAD
"chore: update {$path} from MokoCLI",
=======
"chore: update {$path} from mokoplatform",
>>>>>>> main
$existingSha, $existingSha,
$branch $branch
); );
+179 -41
View File
@@ -9,9 +9,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
<<<<<<< HEAD
* DEFGROUP: MokoCLI.Automation
* INGROUP: MokoCLI.Scripts
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: MokoPlatform.Automation * DEFGROUP: MokoPlatform.Automation
* INGROUP: MokoPlatform.Scripts * INGROUP: MokoPlatform.Scripts
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /automation/bulk_sync.php * PATH: /automation/bulk_sync.php
* BRIEF: Enterprise-grade bulk repository synchronization * BRIEF: Enterprise-grade bulk repository synchronization
*/ */
@@ -21,7 +27,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\{ use MokoCli\{
ApiClient, ApiClient,
AuditLogger, AuditLogger,
CheckpointManager, CheckpointManager,
@@ -42,7 +48,11 @@ use MokoEnterprise\{
/** /**
* Bulk Repository Synchronization Tool * Bulk Repository Synchronization Tool
* *
* Synchronizes moko-platform files across multiple repositories using <<<<<<< HEAD
* Synchronizes MokoCLI files across multiple repositories using
=======
* Synchronizes mokoplatform files across multiple repositories using
>>>>>>> main
* the Enterprise library for robust, audited operations. * the Enterprise library for robust, audited operations.
*/ */
class BulkSync extends CliFramework class BulkSync extends CliFramework
@@ -95,7 +105,11 @@ class BulkSync extends CliFramework
*/ */
protected function run(): int protected function run(): int
{ {
$this->log("🚀 moko-platform Bulk Synchronization v" . self::VERSION, 'INFO'); <<<<<<< HEAD
$this->log("🚀 MokoCLI Bulk Synchronization v" . self::VERSION, 'INFO');
=======
$this->log("🚀 mokoplatform Bulk Synchronization v" . self::VERSION, 'INFO');
>>>>>>> main
// Initialize enterprise components // Initialize enterprise components
if (!$this->initializeComponents()) { if (!$this->initializeComponents()) {
@@ -180,7 +194,11 @@ class BulkSync extends CliFramework
$results['health'] = $this->runHealthChecksAll($org, $repositories); $results['health'] = $this->runHealthChecksAll($org, $repositories);
} }
// Create/update tracking issue in moko-platform <<<<<<< HEAD
// Create/update tracking issue in MokoCLI
=======
// Create/update tracking issue in mokoplatform
>>>>>>> main
$this->createSyncIssue($org, $results); $this->createSyncIssue($org, $results);
// Create/update a failure issue when any repos failed // Create/update a failure issue when any repos failed
@@ -244,7 +262,11 @@ class BulkSync extends CliFramework
* Filter repositories based on include/exclude lists * Filter repositories based on include/exclude lists
*/ */
/** Repositories that are permanently excluded from bulk sync. */ /** Repositories that are permanently excluded from bulk sync. */
private const ALWAYS_EXCLUDE = ['moko-platform', '.github-private']; <<<<<<< HEAD
private const ALWAYS_EXCLUDE = ['MokoCLI', '.github-private'];
=======
private const ALWAYS_EXCLUDE = ['mokoplatform', '.github-private'];
>>>>>>> main
private function filterRepositories(array $repositories, array $include, array $exclude): array private function filterRepositories(array $repositories, array $include, array $exclude): array
{ {
@@ -426,7 +448,11 @@ class BulkSync extends CliFramework
$this->log("", 'ERROR'); $this->log("", 'ERROR');
$this->log("Required Implementation:", 'ERROR'); $this->log("Required Implementation:", 'ERROR');
$this->log(" 1. Clone/fetch target repository", 'ERROR'); $this->log(" 1. Clone/fetch target repository", 'ERROR');
$this->log(" 2. Apply file updates based on moko-platform configuration", 'ERROR'); <<<<<<< HEAD
$this->log(" 2. Apply file updates based on MokoCLI configuration", 'ERROR');
=======
$this->log(" 2. Apply file updates based on mokoplatform configuration", 'ERROR');
>>>>>>> main
$this->log(" 3. Create pull request with changes", 'ERROR'); $this->log(" 3. Create pull request with changes", 'ERROR');
$this->log(" 4. Handle merge conflicts and validation", 'ERROR'); $this->log(" 4. Handle merge conflicts and validation", 'ERROR');
$this->log("", 'ERROR'); $this->log("", 'ERROR');
@@ -837,7 +863,11 @@ class BulkSync extends CliFramework
} }
/** /**
* Ensure all standard moko-platform labels exist on a target repository. <<<<<<< HEAD
* Ensure all standard MokoCLI labels exist on a target repository.
=======
* Ensure all standard mokoplatform labels exist on a target repository.
>>>>>>> main
* *
* Fetches existing labels first (GET) and only POSTs the ones that are * Fetches existing labels first (GET) and only POSTs the ones that are
* missing. This avoids the 422 "already exists" responses that would * missing. This avoids the 422 "already exists" responses that would
@@ -872,7 +902,11 @@ class BulkSync extends CliFramework
// Workflow / Process // Workflow / Process
['automation', '8B4513', 'Automated processes or scripts'], ['automation', '8B4513', 'Automated processes or scripts'],
['moko-platform', 'B60205', 'moko-platform compliance'], <<<<<<< HEAD
['MokoCLI', 'B60205', 'MokoCLI compliance'],
=======
['mokoplatform', 'B60205', 'mokoplatform compliance'],
>>>>>>> main
['needs-review', 'FBCA04', 'Awaiting code review'], ['needs-review', 'FBCA04', 'Awaiting code review'],
['work-in-progress', 'D93F0B', 'Work in progress, not ready for merge'], ['work-in-progress', 'D93F0B', 'Work in progress, not ready for merge'],
['breaking-change', 'D73A4A', 'Breaking API or functionality change'], ['breaking-change', 'D73A4A', 'Breaking API or functionality change'],
@@ -912,8 +946,13 @@ class BulkSync extends CliFramework
['health: poor', 'FF6B6B', 'Health score below 50'], ['health: poor', 'FF6B6B', 'Health score below 50'],
// Sync / Automation (used by bulk_sync, scan_drift, check_repo_health) // Sync / Automation (used by bulk_sync, scan_drift, check_repo_health)
['standards-update', 'B60205', 'moko-platform sync update'], <<<<<<< HEAD
['standards-drift', 'FBCA04', 'Repository drifted from moko-platform'], ['standards-update', 'B60205', 'MokoCLI sync update'],
['standards-drift', 'FBCA04', 'Repository drifted from MokoCLI'],
=======
['standards-update', 'B60205', 'mokoplatform sync update'],
['standards-drift', 'FBCA04', 'Repository drifted from mokoplatform'],
>>>>>>> main
['sync-report', '0075CA', 'Bulk sync run report'], ['sync-report', '0075CA', 'Bulk sync run report'],
['sync-failure', 'D73A4A', 'Bulk sync failure requiring attention'], ['sync-failure', 'D73A4A', 'Bulk sync failure requiring attention'],
['push-failure', 'D73A4A', 'File push failure requiring attention'], ['push-failure', 'D73A4A', 'File push failure requiring attention'],
@@ -925,10 +964,17 @@ class BulkSync extends CliFramework
['type: version', '0E8A16', 'Version-related change'], ['type: version', '0E8A16', 'Version-related change'],
]; ];
// Quick check: if the repo already has the 'moko-platform' label, it was <<<<<<< HEAD
// Quick check: if the repo already has the 'MokoCLI' label, it was
// provisioned previously — skip the expensive full label provisioning. // provisioned previously — skip the expensive full label provisioning.
try { try {
$probe = $this->api->get("/repos/{$org}/{$repo}/labels/moko-platform"); $probe = $this->api->get("/repos/{$org}/{$repo}/labels/MokoCLI");
=======
// Quick check: if the repo already has the 'mokoplatform' label, it was
// provisioned previously — skip the expensive full label provisioning.
try {
$probe = $this->api->get("/repos/{$org}/{$repo}/labels/mokoplatform");
>>>>>>> main
if (!empty($probe['name'])) { if (!empty($probe['name'])) {
return; // already provisioned return; // already provisioned
} }
@@ -1024,7 +1070,11 @@ class BulkSync extends CliFramework
*/ */
private function updateOpenBranches(string $org, string $repo): void private function updateOpenBranches(string $org, string $repo): void
{ {
$syncBranchPrefix = 'chore/sync-moko-platform-'; <<<<<<< HEAD
$syncBranchPrefix = 'chore/sync-MokoCLI-';
=======
$syncBranchPrefix = 'chore/sync-mokoplatform-';
>>>>>>> main
try { try {
$defaultBranch = 'main'; $defaultBranch = 'main';
@@ -1055,7 +1105,11 @@ class BulkSync extends CliFramework
$this->api->post("/repos/{$org}/{$repo}/merges", [ $this->api->post("/repos/{$org}/{$repo}/merges", [
'base' => $branch, 'base' => $branch,
'head' => $defaultBranch, 'head' => $defaultBranch,
'commit_message' => "chore: merge {$defaultBranch} into {$branch} (moko-platform sync)", <<<<<<< HEAD
'commit_message' => "chore: merge {$defaultBranch} into {$branch} (MokoCLI sync)",
=======
'commit_message' => "chore: merge {$defaultBranch} into {$branch} (mokoplatform sync)",
>>>>>>> main
]); ]);
$this->log(" 🔀 Merged {$defaultBranch}{$branch} (PR #{$prNum})", 'INFO'); $this->log(" 🔀 Merged {$defaultBranch}{$branch} (PR #{$prNum})", 'INFO');
} catch (\Exception $e) { } catch (\Exception $e) {
@@ -1076,7 +1130,11 @@ class BulkSync extends CliFramework
/** /**
* Records which sync run touched the repo, the PR number, and the * Records which sync run touched the repo, the PR number, and the
* moko-platform version that was applied — giving each repo a clear audit <<<<<<< HEAD
* MokoCLI version that was applied — giving each repo a clear audit
=======
* mokoplatform version that was applied — giving each repo a clear audit
>>>>>>> main
* trail of what was changed and why. * trail of what was changed and why.
*/ */
/** /**
@@ -1119,16 +1177,29 @@ class BulkSync extends CliFramework
$minor = self::VERSION_MINOR; $minor = self::VERSION_MINOR;
$force = isset($this->options['force']) ? ' *(--force)*' : ''; $force = isset($this->options['force']) ? ' *(--force)*' : '';
$prLink = $this->adapter->getPullRequestWebUrl($org, $repo, $prNumber); $prLink = $this->adapter->getPullRequestWebUrl($org, $repo, $prNumber);
$source = $this->adapter->getRepoWebUrl($org, 'moko-platform'); <<<<<<< HEAD
$branchName = 'chore/sync-moko-platform-v' . $minor; $source = $this->adapter->getRepoWebUrl($org, 'MokoCLI');
$branchName = 'chore/sync-MokoCLI-v' . $minor;
$branchLink = $this->adapter->getBranchWebUrl($org, $repo, $branchName); $branchLink = $this->adapter->getBranchWebUrl($org, $repo, $branchName);
$title = "chore: moko-platform v{$minor} sync tracking"; $title = "chore: MokoCLI v{$minor} sync tracking";
$body = <<<MD $body = <<<MD
## moko-platform Sync Applied ## MokoCLI Sync Applied
A moko-platform bulk sync run has updated files in this repository. A MokoCLI bulk sync run has updated files in this repository.
=======
$source = $this->adapter->getRepoWebUrl($org, 'mokoplatform');
$branchName = 'chore/sync-mokoplatform-v' . $minor;
$branchLink = $this->adapter->getBranchWebUrl($org, $repo, $branchName);
$title = "chore: mokoplatform v{$minor} sync tracking";
$body = <<<MD
## mokoplatform Sync Applied
A mokoplatform bulk sync run has updated files in this repository.
>>>>>>> main
| Field | Value | | Field | Value |
|-------|-------| |-------|-------|
@@ -1144,13 +1215,21 @@ class BulkSync extends CliFramework
Protected files (README, CHANGELOG, GOVERNANCE, etc.) were not overwritten. Protected files (README, CHANGELOG, GOVERNANCE, etc.) were not overwritten.
--- ---
*Updated automatically by [moko-platform]({$source}) `bulk_sync.php`* <<<<<<< HEAD
*Updated automatically by [MokoCLI]({$source}) `bulk_sync.php`*
=======
*Updated automatically by [mokoplatform]({$source}) `bulk_sync.php`*
>>>>>>> main
MD; MD;
// Dedent heredoc // Dedent heredoc
$body = preg_replace('/^ /m', '', $body); $body = preg_replace('/^ /m', '', $body);
$labelNames = ['standards-update', 'moko-platform', 'type: chore', 'automation']; <<<<<<< HEAD
$labelNames = ['standards-update', 'MokoCLI', 'type: chore', 'automation'];
=======
$labelNames = ['standards-update', 'mokoplatform', 'type: chore', 'automation'];
>>>>>>> main
$labels = $this->resolveLabelIds($org, $repo, $labelNames); $labels = $this->resolveLabelIds($org, $repo, $labelNames);
try { try {
@@ -1213,7 +1292,11 @@ class BulkSync extends CliFramework
} }
/** /**
* Create a tracking issue in moko-platform for this sync run. <<<<<<< HEAD
* Create a tracking issue in MokoCLI for this sync run.
=======
* Create a tracking issue in mokoplatform for this sync run.
>>>>>>> main
*/ */
private function createSyncIssue(string $org, array $results): void private function createSyncIssue(string $org, array $results): void
{ {
@@ -1232,7 +1315,11 @@ class BulkSync extends CliFramework
$issues = $results['issues'] ?? []; $issues = $results['issues'] ?? [];
// Stable title — no timestamp so repeated runs update a single issue // Stable title — no timestamp so repeated runs update a single issue
$title = "sync: moko-platform v" . self::VERSION_MINOR . " bulk sync report"; <<<<<<< HEAD
$title = "sync: MokoCLI v" . self::VERSION_MINOR . " bulk sync report";
=======
$title = "sync: mokoplatform v" . self::VERSION_MINOR . " bulk sync report";
>>>>>>> main
$protection = $results['protection'] ?? []; $protection = $results['protection'] ?? [];
$hasProtect = !empty($protection); $hasProtect = !empty($protection);
@@ -1281,7 +1368,11 @@ class BulkSync extends CliFramework
: "|---|---|---|---|"; : "|---|---|---|---|";
$body = <<<MD $body = <<<MD
## moko-platform Bulk Sync Report <<<<<<< HEAD
## MokoCLI Bulk Sync Report
=======
## mokoplatform Bulk Sync Report
>>>>>>> main
**Organisation:** `{$org}` **Organisation:** `{$org}`
**Triggered:** {$now}{$force} **Triggered:** {$now}{$force}
@@ -1301,7 +1392,11 @@ class BulkSync extends CliFramework
try { try {
// Search for existing issue by label — any state so we can reopen closed ones // Search for existing issue by label — any state so we can reopen closed ones
$existing = $this->api->get("/repos/{$org}/moko-platform/issues", [ <<<<<<< HEAD
$existing = $this->api->get("/repos/{$org}/MokoCLI/issues", [
=======
$existing = $this->api->get("/repos/{$org}/mokoplatform/issues", [
>>>>>>> main
'labels' => 'sync-report', 'labels' => 'sync-report',
'state' => 'all', 'state' => 'all',
'per_page' => 1, 'per_page' => 1,
@@ -1309,8 +1404,13 @@ class BulkSync extends CliFramework
'direction' => 'desc', 'direction' => 'desc',
]); ]);
$labelNames = ['sync-report', 'moko-platform', 'type: chore', 'automation']; <<<<<<< HEAD
$labels = $this->resolveLabelIds($org, 'moko-platform', $labelNames); $labelNames = ['sync-report', 'MokoCLI', 'type: chore', 'automation'];
$labels = $this->resolveLabelIds($org, 'MokoCLI', $labelNames);
=======
$labelNames = ['sync-report', 'mokoplatform', 'type: chore', 'automation'];
$labels = $this->resolveLabelIds($org, 'mokoplatform', $labelNames);
>>>>>>> main
$existing = array_values($existing); $existing = array_values($existing);
if (!empty($existing) && isset($existing[0]['number'])) { if (!empty($existing) && isset($existing[0]['number'])) {
@@ -1319,22 +1419,38 @@ class BulkSync extends CliFramework
if (($existing[0]['state'] ?? 'open') === 'closed') { if (($existing[0]['state'] ?? 'open') === 'closed') {
$patch['state'] = 'open'; $patch['state'] = 'open';
} }
$this->api->patch("/repos/{$org}/moko-platform/issues/{$issueNumber}", $patch); <<<<<<< HEAD
$this->api->patch("/repos/{$org}/MokoCLI/issues/{$issueNumber}", $patch);
try { try {
$this->api->post("/repos/{$org}/moko-platform/issues/{$issueNumber}/labels", ['labels' => $labels]); $this->api->post("/repos/{$org}/MokoCLI/issues/{$issueNumber}/labels", ['labels' => $labels]);
} catch (\Exception $le) { } catch (\Exception $le) {
/* non-fatal */ /* non-fatal */
} }
$this->log("📋 Sync report issue updated: {$org}/moko-platform#{$issueNumber}", 'INFO'); $this->log("📋 Sync report issue updated: {$org}/MokoCLI#{$issueNumber}", 'INFO');
} else { } else {
$issue = $this->api->post("/repos/{$org}/moko-platform/issues", [ $issue = $this->api->post("/repos/{$org}/MokoCLI/issues", [
=======
$this->api->patch("/repos/{$org}/mokoplatform/issues/{$issueNumber}", $patch);
try {
$this->api->post("/repos/{$org}/mokoplatform/issues/{$issueNumber}/labels", ['labels' => $labels]);
} catch (\Exception $le) {
/* non-fatal */
}
$this->log("📋 Sync report issue updated: {$org}/mokoplatform#{$issueNumber}", 'INFO');
} else {
$issue = $this->api->post("/repos/{$org}/mokoplatform/issues", [
>>>>>>> main
'title' => $title, 'title' => $title,
'body' => $body, 'body' => $body,
'labels' => $labels, 'labels' => $labels,
'assignees' => ['jmiller'], 'assignees' => ['jmiller'],
]); ]);
$issueNumber = $issue['number'] ?? '?'; $issueNumber = $issue['number'] ?? '?';
$this->log("📋 Sync report issue created: {$org}/moko-platform#{$issueNumber}", 'INFO'); <<<<<<< HEAD
$this->log("📋 Sync report issue created: {$org}/MokoCLI#{$issueNumber}", 'INFO');
=======
$this->log("📋 Sync report issue created: {$org}/mokoplatform#{$issueNumber}", 'INFO');
>>>>>>> main
} }
} catch (\Exception $e) { } catch (\Exception $e) {
$this->log("⚠️ Failed to create/update sync report issue: " . $e->getMessage(), 'WARN'); $this->log("⚠️ Failed to create/update sync report issue: " . $e->getMessage(), 'WARN');
@@ -1342,7 +1458,11 @@ class BulkSync extends CliFramework
} }
/** /**
* Create or update a failure issue in moko-platform when repos fail to sync. <<<<<<< HEAD
* Create or update a failure issue in MokoCLI when repos fail to sync.
=======
* Create or update a failure issue in mokoplatform when repos fail to sync.
>>>>>>> main
* Uses the 'sync-failure' label so it is distinct from the run-report issue. * Uses the 'sync-failure' label so it is distinct from the run-report issue.
* Reopens a closed issue rather than creating a duplicate. * Reopens a closed issue rather than creating a duplicate.
*/ */
@@ -1388,7 +1508,11 @@ class BulkSync extends CliFramework
$body = preg_replace('/^ /m', '', $body); $body = preg_replace('/^ /m', '', $body);
try { try {
$existing = $this->api->get("/repos/{$org}/moko-platform/issues", [ <<<<<<< HEAD
$existing = $this->api->get("/repos/{$org}/MokoCLI/issues", [
=======
$existing = $this->api->get("/repos/{$org}/mokoplatform/issues", [
>>>>>>> main
'labels' => 'sync-failure', 'labels' => 'sync-failure',
'state' => 'all', 'state' => 'all',
'per_page' => 1, 'per_page' => 1,
@@ -1403,17 +1527,31 @@ class BulkSync extends CliFramework
if (($existing[0]['state'] ?? 'open') === 'closed') { if (($existing[0]['state'] ?? 'open') === 'closed') {
$patch['state'] = 'open'; $patch['state'] = 'open';
} }
$this->api->patch("/repos/{$org}/moko-platform/issues/{$num}", $patch); <<<<<<< HEAD
$this->log("🚨 Failure issue #{$num} updated: {$org}/moko-platform#{$num}", 'WARN'); $this->api->patch("/repos/{$org}/MokoCLI/issues/{$num}", $patch);
$this->log("🚨 Failure issue #{$num} updated: {$org}/MokoCLI#{$num}", 'WARN');
} else { } else {
$issue = $this->api->post("/repos/{$org}/moko-platform/issues", [ $issue = $this->api->post("/repos/{$org}/MokoCLI/issues", [
'title' => $title, 'title' => $title,
'body' => $body, 'body' => $body,
'labels' => $this->resolveLabelIds($org, 'moko-platform', ['sync-failure']), 'labels' => $this->resolveLabelIds($org, 'MokoCLI', ['sync-failure']),
'assignees' => ['jmiller'], 'assignees' => ['jmiller'],
]); ]);
$num = $issue['number'] ?? '?'; $num = $issue['number'] ?? '?';
$this->log("🚨 Failure issue created: {$org}/moko-platform#{$num}", 'WARN'); $this->log("🚨 Failure issue created: {$org}/MokoCLI#{$num}", 'WARN');
=======
$this->api->patch("/repos/{$org}/mokoplatform/issues/{$num}", $patch);
$this->log("🚨 Failure issue #{$num} updated: {$org}/mokoplatform#{$num}", 'WARN');
} else {
$issue = $this->api->post("/repos/{$org}/mokoplatform/issues", [
'title' => $title,
'body' => $body,
'labels' => $this->resolveLabelIds($org, 'mokoplatform', ['sync-failure']),
'assignees' => ['jmiller'],
]);
$num = $issue['number'] ?? '?';
$this->log("🚨 Failure issue created: {$org}/mokoplatform#{$num}", 'WARN');
>>>>>>> main
} }
} catch (\Exception $e) { } catch (\Exception $e) {
$this->log("⚠️ Could not create/update failure issue: " . $e->getMessage(), 'WARN'); $this->log("⚠️ Could not create/update failure issue: " . $e->getMessage(), 'WARN');
+123 -123
View File
@@ -1,123 +1,123 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech> # Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# BRIEF: Trigger a workflow across all client-waas repos in a Gitea org # BRIEF: Trigger a workflow across all client-waas repos in a Gitea org
set -euo pipefail set -euo pipefail
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Usage # Usage
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
usage() { usage() {
cat <<EOF cat <<EOF
Usage: $(basename "$0") GITEA_URL TOKEN ORG WORKFLOW [REF] [INPUTS] Usage: $(basename "$0") GITEA_URL TOKEN ORG WORKFLOW [REF] [INPUTS]
Arguments: Arguments:
GITEA_URL Base URL of the Gitea instance (e.g. https://git.mokoconsulting.tech) GITEA_URL Base URL of the Gitea instance (e.g. https://git.mokoconsulting.tech)
TOKEN Gitea API token with repo/action permissions TOKEN Gitea API token with repo/action permissions
ORG Organisation or user that owns the repos ORG Organisation or user that owns the repos
WORKFLOW Workflow filename to trigger (e.g. dependency-audit.yml) WORKFLOW Workflow filename to trigger (e.g. dependency-audit.yml)
REF Branch ref to run against (default: main) REF Branch ref to run against (default: main)
INPUTS Optional JSON object of workflow inputs (e.g. '{"dry_run":"true"}') INPUTS Optional JSON object of workflow inputs (e.g. '{"dry_run":"true"}')
Example: Example:
$(basename "$0") https://git.mokoconsulting.tech abc123 MokoConsulting dependency-audit.yml main '{"notify":"true"}' $(basename "$0") https://git.mokoconsulting.tech abc123 MokoConsulting dependency-audit.yml main '{"notify":"true"}'
EOF EOF
exit 1 exit 1
} }
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Argument parsing # Argument parsing
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
if [ $# -lt 4 ]; then if [ $# -lt 4 ]; then
usage usage
fi fi
GITEA_URL="${1%/}" GITEA_URL="${1%/}"
TOKEN="$2" TOKEN="$2"
ORG="$3" ORG="$3"
WORKFLOW="$4" WORKFLOW="$4"
REF="${5:-main}" REF="${5:-main}"
INPUTS="${6:-{\}}" INPUTS="${6:-{\}}"
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Fetch all repos in the org, paginated # Fetch all repos in the org, paginated
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
echo "Fetching repos for org '${ORG}' on ${GITEA_URL} ..." echo "Fetching repos for org '${ORG}' on ${GITEA_URL} ..."
PAGE=1 PAGE=1
LIMIT=50 LIMIT=50
ALL_REPOS="" ALL_REPOS=""
while true; do while true; do
RESPONSE=$(curl -s \ RESPONSE=$(curl -s \
-H "Authorization: token ${TOKEN}" \ -H "Authorization: token ${TOKEN}" \
-H "Accept: application/json" \ -H "Accept: application/json" \
"${GITEA_URL}/api/v1/orgs/${ORG}/repos?page=${PAGE}&limit=${LIMIT}") "${GITEA_URL}/api/v1/orgs/${ORG}/repos?page=${PAGE}&limit=${LIMIT}")
# Break if empty array # Break if empty array
COUNT=$(echo "$RESPONSE" | jq -r 'length') COUNT=$(echo "$RESPONSE" | jq -r 'length')
if [ "$COUNT" -eq 0 ]; then if [ "$COUNT" -eq 0 ]; then
break break
fi fi
NAMES=$(echo "$RESPONSE" | jq -r '.[].name') NAMES=$(echo "$RESPONSE" | jq -r '.[].name')
ALL_REPOS="${ALL_REPOS}${NAMES}"$'\n' ALL_REPOS="${ALL_REPOS}${NAMES}"$'\n'
if [ "$COUNT" -lt "$LIMIT" ]; then if [ "$COUNT" -lt "$LIMIT" ]; then
break break
fi fi
PAGE=$((PAGE + 1)) PAGE=$((PAGE + 1))
done done
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Filter for client-waas repos # Filter for client-waas repos
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
CLIENT_REPOS=$(echo "$ALL_REPOS" | grep 'client-waas' | sort || true) CLIENT_REPOS=$(echo "$ALL_REPOS" | grep 'client-waas' | sort || true)
if [ -z "$CLIENT_REPOS" ]; then if [ -z "$CLIENT_REPOS" ]; then
echo "No client-waas repos found in org '${ORG}'." echo "No client-waas repos found in org '${ORG}'."
exit 0 exit 0
fi fi
TOTAL=$(echo "$CLIENT_REPOS" | wc -l | tr -d ' ') TOTAL=$(echo "$CLIENT_REPOS" | wc -l | tr -d ' ')
echo "Found ${TOTAL} client-waas repo(s). Triggering workflow '${WORKFLOW}' (ref: ${REF}) ..." echo "Found ${TOTAL} client-waas repo(s). Triggering workflow '${WORKFLOW}' (ref: ${REF}) ..."
echo "" echo ""
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Trigger workflow for each repo # Trigger workflow for each repo
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
SUCCESS=0 SUCCESS=0
FAIL=0 FAIL=0
while IFS= read -r REPO; do while IFS= read -r REPO; do
[ -z "$REPO" ] && continue [ -z "$REPO" ] && continue
PAYLOAD=$(jq -n --arg ref "$REF" --argjson inputs "$INPUTS" '{ref: $ref, inputs: $inputs}') PAYLOAD=$(jq -n --arg ref "$REF" --argjson inputs "$INPUTS" '{ref: $ref, inputs: $inputs}')
HTTP_CODE=$(curl -s -o /dev/null -w '%{http_code}' \ HTTP_CODE=$(curl -s -o /dev/null -w '%{http_code}' \
-X POST \ -X POST \
-H "Authorization: token ${TOKEN}" \ -H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d "$PAYLOAD" \ -d "$PAYLOAD" \
"${GITEA_URL}/api/v1/repos/${ORG}/${REPO}/actions/workflows/${WORKFLOW}/dispatches") "${GITEA_URL}/api/v1/repos/${ORG}/${REPO}/actions/workflows/${WORKFLOW}/dispatches")
if [ "$HTTP_CODE" -eq 204 ] || [ "$HTTP_CODE" -eq 201 ]; then if [ "$HTTP_CODE" -eq 204 ] || [ "$HTTP_CODE" -eq 201 ]; then
echo " [OK] ${ORG}/${REPO} (HTTP ${HTTP_CODE})" echo " [OK] ${ORG}/${REPO} (HTTP ${HTTP_CODE})"
SUCCESS=$((SUCCESS + 1)) SUCCESS=$((SUCCESS + 1))
else else
echo " [FAIL] ${ORG}/${REPO} (HTTP ${HTTP_CODE})" echo " [FAIL] ${ORG}/${REPO} (HTTP ${HTTP_CODE})"
FAIL=$((FAIL + 1)) FAIL=$((FAIL + 1))
fi fi
done <<< "$CLIENT_REPOS" done <<< "$CLIENT_REPOS"
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Summary # Summary
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
echo "" echo ""
echo "Done. Success: ${SUCCESS} | Failed: ${FAIL} | Total: ${TOTAL}" echo "Done. Success: ${SUCCESS} | Failed: ${FAIL} | Total: ${TOTAL}"
if [ "$FAIL" -gt 0 ]; then if [ "$FAIL" -gt 0 ]; then
exit 1 exit 1
fi fi
+237
View File
@@ -0,0 +1,237 @@
#!/usr/bin/env bash
# ============================================================================
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Automation.CI
# INGROUP: MokoCLI.Automation
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# 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
+23 -9
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
<<<<<<< HEAD
* DEFGROUP: MokoCLI.Automation
* INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: MokoPlatform.Automation * DEFGROUP: MokoPlatform.Automation
* INGROUP: MokoPlatform * INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /automation/enrich_manifest_xml.php * PATH: /automation/enrich_manifest_xml.php
* BRIEF: Enrich XML manifests with repo-specific build and deploy details * BRIEF: Enrich XML manifests with repo-specific build and deploy details
* *
@@ -21,8 +27,8 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
use MokoEnterprise\MokoStandardsParser; use MokoCli\ManifestParser;
class EnrichManifestXmlCli extends CliFramework class EnrichManifestXmlCli extends CliFramework
{ {
@@ -43,10 +49,14 @@ class EnrichManifestXmlCli extends CliFramework
$skipStr = $this->getArgument('--skip'); $skipStr = $this->getArgument('--skip');
$skipRepos = $skipStr !== '' ? array_map('trim', explode(',', $skipStr)) : []; $skipRepos = $skipStr !== '' ? array_map('trim', explode(',', $skipStr)) : [];
$parser = new MokoStandardsParser(); $parser = new ManifestParser();
$tmpBase = sys_get_temp_dir() . '/moko-enrich-' . getmypid(); $tmpBase = sys_get_temp_dir() . '/moko-enrich-' . getmypid();
echo "=== moko-platform XML Manifest Enrichment ===\n"; <<<<<<< HEAD
echo "=== MokoCLI XML Manifest Enrichment ===\n";
=======
echo "=== mokoplatform XML Manifest Enrichment ===\n";
>>>>>>> main
echo "Mode: " . ($this->dryRun ? "DRY RUN" : "LIVE") . "\n"; echo "Mode: " . ($this->dryRun ? "DRY RUN" : "LIVE") . "\n";
if (!empty($skipRepos)) { if (!empty($skipRepos)) {
echo "Skipping: " . implode(', ', $skipRepos) . "\n"; echo "Skipping: " . implode(', ', $skipRepos) . "\n";
@@ -97,7 +107,11 @@ class EnrichManifestXmlCli extends CliFramework
} }
$manifestPath = "{$workDir}/.mokogitea/manifest.xml"; $manifestPath = "{$workDir}/.mokogitea/manifest.xml";
if (!file_exists($manifestPath) || !str_contains(file_get_contents($manifestPath), '<moko-platform')) { <<<<<<< HEAD
if (!file_exists($manifestPath) || !str_contains(file_get_contents($manifestPath), '<MokoCLI')) {
=======
if (!file_exists($manifestPath) || !str_contains(file_get_contents($manifestPath), '<mokoplatform')) {
>>>>>>> main
echo "SKIP (no XML manifest)\n"; echo "SKIP (no XML manifest)\n";
$stats['skipped']++; $stats['skipped']++;
$this->rmTree($workDir); $this->rmTree($workDir);
@@ -113,8 +127,8 @@ class EnrichManifestXmlCli extends CliFramework
} }
$enrichment['build']['language'] = $enrichment['build']['language'] $enrichment['build']['language'] = $enrichment['build']['language']
?? $repo['language'] ?? $repo['language']
?? MokoStandardsParser::platformLanguage($platform); ?? ManifestParser::platformLanguage($platform);
$enrichment['build']['package_type'] = $enrichment['build']['package_type'] ?? MokoStandardsParser::platformPackageType($platform); $enrichment['build']['package_type'] = $enrichment['build']['package_type'] ?? ManifestParser::platformPackageType($platform);
$enrichedXml = $this->enrichManifestXml($existingXml, $enrichment); $enrichedXml = $this->enrichManifestXml($existingXml, $enrichment);
$dc = count($enrichment['deploy'] ?? []); $dc = count($enrichment['deploy'] ?? []);
@@ -312,7 +326,7 @@ class EnrichManifestXmlCli extends CliFramework
return $xml; return $xml;
} }
$ns = MokoStandardsParser::NAMESPACE_URI; $ns = ManifestParser::NAMESPACE_URI;
$root = $dom->documentElement; $root = $dom->documentElement;
foreach (['build', 'deploy', 'scripts'] as $tag) { foreach (['build', 'deploy', 'scripts'] as $tag) {
+23 -9
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
<<<<<<< HEAD
* DEFGROUP: MokoCLI.Automation
* INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: MokoPlatform.Automation * DEFGROUP: MokoPlatform.Automation
* INGROUP: MokoPlatform * INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /automation/enrich_mokostandards_xml.php * PATH: /automation/enrich_mokostandards_xml.php
* BRIEF: Enrich XML manifests with repo-specific build and deploy details * BRIEF: Enrich XML manifests with repo-specific build and deploy details
* *
@@ -21,8 +27,8 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
use MokoEnterprise\MokoStandardsParser; use MokoCli\ManifestParser;
class EnrichMokostandardsXmlCli extends CliFramework class EnrichMokostandardsXmlCli extends CliFramework
{ {
@@ -43,10 +49,14 @@ class EnrichMokostandardsXmlCli extends CliFramework
$skipStr = $this->getArgument('--skip'); $skipStr = $this->getArgument('--skip');
$skipRepos = $skipStr !== '' ? array_map('trim', explode(',', $skipStr)) : []; $skipRepos = $skipStr !== '' ? array_map('trim', explode(',', $skipStr)) : [];
$parser = new MokoStandardsParser(); $parser = new ManifestParser();
$tmpBase = sys_get_temp_dir() . '/moko-enrich-' . getmypid(); $tmpBase = sys_get_temp_dir() . '/moko-enrich-' . getmypid();
echo "=== moko-platform XML Manifest Enrichment ===\n"; <<<<<<< HEAD
echo "=== MokoCLI XML Manifest Enrichment ===\n";
=======
echo "=== mokoplatform XML Manifest Enrichment ===\n";
>>>>>>> main
echo "Mode: " . ($this->dryRun ? "DRY RUN" : "LIVE") . "\n"; echo "Mode: " . ($this->dryRun ? "DRY RUN" : "LIVE") . "\n";
if (!empty($skipRepos)) { if (!empty($skipRepos)) {
echo "Skipping: " . implode(', ', $skipRepos) . "\n"; echo "Skipping: " . implode(', ', $skipRepos) . "\n";
@@ -97,7 +107,11 @@ class EnrichMokostandardsXmlCli extends CliFramework
} }
$manifestPath = "{$workDir}/.mokogitea/manifest.xml"; $manifestPath = "{$workDir}/.mokogitea/manifest.xml";
if (!file_exists($manifestPath) || !str_contains(file_get_contents($manifestPath), '<moko-platform')) { <<<<<<< HEAD
if (!file_exists($manifestPath) || !str_contains(file_get_contents($manifestPath), '<MokoCLI')) {
=======
if (!file_exists($manifestPath) || !str_contains(file_get_contents($manifestPath), '<mokoplatform')) {
>>>>>>> main
echo "SKIP (no XML manifest)\n"; echo "SKIP (no XML manifest)\n";
$stats['skipped']++; $stats['skipped']++;
$this->rmTree($workDir); $this->rmTree($workDir);
@@ -113,8 +127,8 @@ class EnrichMokostandardsXmlCli extends CliFramework
} }
$enrichment['build']['language'] = $enrichment['build']['language'] $enrichment['build']['language'] = $enrichment['build']['language']
?? $repo['language'] ?? $repo['language']
?? MokoStandardsParser::platformLanguage($platform); ?? ManifestParser::platformLanguage($platform);
$enrichment['build']['package_type'] = $enrichment['build']['package_type'] ?? MokoStandardsParser::platformPackageType($platform); $enrichment['build']['package_type'] = $enrichment['build']['package_type'] ?? ManifestParser::platformPackageType($platform);
$enrichedXml = $this->enrichManifestXml($existingXml, $enrichment); $enrichedXml = $this->enrichManifestXml($existingXml, $enrichment);
$dc = count($enrichment['deploy'] ?? []); $dc = count($enrichment['deploy'] ?? []);
@@ -315,7 +329,7 @@ class EnrichMokostandardsXmlCli extends CliFramework
return $xml; return $xml;
} }
$ns = MokoStandardsParser::NAMESPACE_URI; $ns = ManifestParser::NAMESPACE_URI;
$root = $dom->documentElement; $root = $dom->documentElement;
foreach (['build', 'deploy', 'scripts'] as $tag) { foreach (['build', 'deploy', 'scripts'] as $tag) {
+7 -1
View File
@@ -2,9 +2,15 @@
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech> Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
SPDX-License-Identifier: GPL-3.0-or-later SPDX-License-Identifier: GPL-3.0-or-later
FILE INFORMATION FILE INFORMATION
<<<<<<< HEAD
DEFGROUP: MokoCLI.Index
INGROUP: MokoCLI.Automation
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
DEFGROUP: MokoPlatform.Index DEFGROUP: MokoPlatform.Index
INGROUP: MokoPlatform.Automation INGROUP: MokoPlatform.Automation
REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
PATH: /automation/index.md PATH: /automation/index.md
BRIEF: Automation directory index BRIEF: Automation directory index
--> -->
+23 -9
View File
@@ -8,16 +8,26 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
<<<<<<< HEAD
* DEFGROUP: MokoCLI.Automation
* INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: MokoPlatform.Automation * DEFGROUP: MokoPlatform.Automation
* INGROUP: MokoPlatform * INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /automation/migrate_to_gitea.php * PATH: /automation/migrate_to_gitea.php
* BRIEF: Migrate repositories from GitHub to self-hosted Gitea instance * BRIEF: Migrate repositories from GitHub to self-hosted Gitea instance
* *
* USAGE * USAGE
* php automation/migrate_to_gitea.php --dry-run * php automation/migrate_to_gitea.php --dry-run
* php automation/migrate_to_gitea.php --repos MokoCRM MokoDoliMods * php automation/migrate_to_gitea.php --repos MokoCRM MokoDoliMods
* php automation/migrate_to_gitea.php --exclude moko-platform --skip-archived <<<<<<< HEAD
* php automation/migrate_to_gitea.php --exclude MokoCLI --skip-archived
=======
* php automation/migrate_to_gitea.php --exclude mokoplatform --skip-archived
>>>>>>> main
* php automation/migrate_to_gitea.php --resume * php automation/migrate_to_gitea.php --resume
*/ */
@@ -25,12 +35,12 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
use MokoEnterprise\CheckpointManager; use MokoCli\CheckpointManager;
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
use MokoEnterprise\Config; use MokoCli\Config;
use MokoEnterprise\PlatformAdapterFactory; use MokoCli\PlatformAdapterFactory;
use MokoEnterprise\GitHubAdapter; use MokoCli\GitHubAdapter;
use MokoEnterprise\MokoGiteaAdapter; use MokoCli\MokoGiteaAdapter;
/** /**
* Gitea Migration Script * Gitea Migration Script
@@ -278,7 +288,11 @@ class MigrateToGitea extends CliFramework
try { try {
$this->gitea->createIssue( $this->gitea->createIssue(
$giteaOrg, $giteaOrg,
'moko-platform', <<<<<<< HEAD
'MokoCLI',
=======
'mokoplatform',
>>>>>>> main
'chore: GitHub → Gitea migration report — ' . count($results['migrated']) . ' repos migrated', 'chore: GitHub → Gitea migration report — ' . count($results['migrated']) . ' repos migrated',
$report, $report,
['labels' => ['automation', 'type: chore']] ['labels' => ['automation', 'type: chore']]
+93 -22
View File
@@ -9,9 +9,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
<<<<<<< HEAD
* DEFGROUP: MokoCLI.Automation
* INGROUP: MokoCLI.Scripts
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: MokoPlatform.Automation * DEFGROUP: MokoPlatform.Automation
* INGROUP: MokoPlatform.Scripts * INGROUP: MokoPlatform.Scripts
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /automation/push_files.php * PATH: /automation/push_files.php
* BRIEF: Push one or more specific files to one or more remote repositories * BRIEF: Push one or more specific files to one or more remote repositories
*/ */
@@ -21,7 +27,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\{ use MokoCli\{
ApiClient, ApiClient,
AuditLogger, AuditLogger,
CliFramework, CliFramework,
@@ -35,7 +41,11 @@ use MokoEnterprise\{
/** /**
* Targeted File Push Tool * Targeted File Push Tool
* *
* Pushes one or more specific files from moko-platform templates to one or <<<<<<< HEAD
* Pushes one or more specific files from MokoCLI templates to one or
=======
* Pushes one or more specific files from mokoplatform templates to one or
>>>>>>> main
* more remote repositories — without running a full sync. * more remote repositories — without running a full sync.
* *
* Files are specified by their destination path as they appear in the target * Files are specified by their destination path as they appear in the target
@@ -81,7 +91,11 @@ class PushFiles extends CliFramework
*/ */
protected function run(): int protected function run(): int
{ {
$this->log('📦 moko-platform File Push v' . self::VERSION, 'INFO'); <<<<<<< HEAD
$this->log('📦 MokoCLI File Push v' . self::VERSION, 'INFO');
=======
$this->log('📦 mokoplatform File Push v' . self::VERSION, 'INFO');
>>>>>>> main
if (!$this->initializeComponents()) { if (!$this->initializeComponents()) {
return 1; return 1;
@@ -230,7 +244,8 @@ class PushFiles extends CliFramework
{ {
// Read platform from repo's .mokogitea/manifest.xml via API // Read platform from repo's .mokogitea/manifest.xml via API
try { try {
$manifestData = $this->adapter->getFileContent($org, $repo, '.mokogitea/manifest.xml', 'main'); $fileInfo = $this->adapter->getFileContents($org, $repo, '.mokogitea/manifest.xml', 'main');
$manifestData = isset($fileInfo['content']) ? base64_decode($fileInfo['content']) : '';
if (!empty($manifestData)) { if (!empty($manifestData)) {
$xml = @simplexml_load_string($manifestData); $xml = @simplexml_load_string($manifestData);
if ($xml !== false) { if ($xml !== false) {
@@ -336,7 +351,11 @@ class PushFiles extends CliFramework
$prNumber = null; $prNumber = null;
if (!$direct) { if (!$direct) {
$prTitle = "chore: push " . count($entries) . " file(s) from moko-platform"; <<<<<<< HEAD
$prTitle = "chore: push " . count($entries) . " file(s) from MokoCLI";
=======
$prTitle = "chore: push " . count($entries) . " file(s) from mokoplatform";
>>>>>>> main
$prBody = $this->buildPRBody($entries); $prBody = $this->buildPRBody($entries);
$pr = $this->adapter->createPullRequest( $pr = $this->adapter->createPullRequest(
$org, $org,
@@ -413,7 +432,11 @@ class PushFiles extends CliFramework
$message = !empty($customMessage) $message = !empty($customMessage)
? $customMessage ? $customMessage
: "chore: update {$destPath} from moko-platform"; <<<<<<< HEAD
: "chore: update {$destPath} from MokoCLI";
=======
: "chore: update {$destPath} from mokoplatform";
>>>>>>> main
// Fetch existing file SHA (needed for updates) // Fetch existing file SHA (needed for updates)
$existingSha = null; $existingSha = null;
@@ -456,9 +479,15 @@ class PushFiles extends CliFramework
): void { ): void {
$now = gmdate('Y-m-d H:i:s') . ' UTC'; $now = gmdate('Y-m-d H:i:s') . ' UTC';
$version = self::VERSION; $version = self::VERSION;
$source = $this->adapter->getRepoWebUrl($org, 'moko-platform'); <<<<<<< HEAD
$source = $this->adapter->getRepoWebUrl($org, 'MokoCLI');
$title = "chore: moko-platform file push tracking"; $title = "chore: MokoCLI file push tracking";
=======
$source = $this->adapter->getRepoWebUrl($org, 'mokoplatform');
$title = "chore: mokoplatform file push tracking";
>>>>>>> main
$deliveryLine = $prNumber !== null $deliveryLine = $prNumber !== null
? "| **Pull request** | [#{$prNumber}](" . $this->adapter->getPullRequestWebUrl($org, $repo, $prNumber) . ") |" ? "| **Pull request** | [#{$prNumber}](" . $this->adapter->getPullRequestWebUrl($org, $repo, $prNumber) . ") |"
@@ -470,9 +499,15 @@ class PushFiles extends CliFramework
)); ));
$body = <<<MD $body = <<<MD
## moko-platform File Push <<<<<<< HEAD
## MokoCLI File Push
One or more files were pushed to this repository from moko-platform. One or more files were pushed to this repository from MokoCLI.
=======
## mokoplatform File Push
One or more files were pushed to this repository from mokoplatform.
>>>>>>> main
| Field | Value | | Field | Value |
|-------|-------| |-------|-------|
@@ -486,12 +521,20 @@ class PushFiles extends CliFramework
{$fileRows} {$fileRows}
--- ---
*Generated automatically by [moko-platform]({$source}) `push_files.php`* <<<<<<< HEAD
*Generated automatically by [MokoCLI]({$source}) `push_files.php`*
=======
*Generated automatically by [mokoplatform]({$source}) `push_files.php`*
>>>>>>> main
MD; MD;
$body = preg_replace('/^ /m', '', $body); $body = preg_replace('/^ /m', '', $body);
$labels = ['standards-update', 'moko-platform', 'type: chore', 'automation']; <<<<<<< HEAD
$labels = ['standards-update', 'MokoCLI', 'type: chore', 'automation'];
=======
$labels = ['standards-update', 'mokoplatform', 'type: chore', 'automation'];
>>>>>>> main
try { try {
$existing = $this->api->get("/repos/{$org}/{$repo}/issues", [ $existing = $this->api->get("/repos/{$org}/{$repo}/issues", [
@@ -549,7 +592,11 @@ class PushFiles extends CliFramework
} }
/** /**
* Create or update a failure issue in moko-platform when repos fail to receive files. <<<<<<< HEAD
* Create or update a failure issue in MokoCLI when repos fail to receive files.
=======
* Create or update a failure issue in mokoplatform when repos fail to receive files.
>>>>>>> main
* Uses the 'push-failure' label. Reopens a closed issue rather than creating a duplicate. * Uses the 'push-failure' label. Reopens a closed issue rather than creating a duplicate.
*/ */
private function createFailureIssue(string $org, array $results): void private function createFailureIssue(string $org, array $results): void
@@ -597,7 +644,11 @@ class PushFiles extends CliFramework
$body = preg_replace('/^ /m', '', $body); $body = preg_replace('/^ /m', '', $body);
try { try {
$existing = $this->api->get("/repos/{$org}/moko-platform/issues", [ <<<<<<< HEAD
$existing = $this->api->get("/repos/{$org}/MokoCLI/issues", [
=======
$existing = $this->api->get("/repos/{$org}/mokoplatform/issues", [
>>>>>>> main
'labels' => 'push-failure', 'labels' => 'push-failure',
'state' => 'all', 'state' => 'all',
'per_page' => 1, 'per_page' => 1,
@@ -612,17 +663,28 @@ class PushFiles extends CliFramework
if (($existing[0]['state'] ?? 'open') === 'closed') { if (($existing[0]['state'] ?? 'open') === 'closed') {
$patch['state'] = 'open'; $patch['state'] = 'open';
} }
$this->api->patch("/repos/{$org}/moko-platform/issues/{$num}", $patch); <<<<<<< HEAD
$this->log("🚨 Failure issue #{$num} updated: {$org}/moko-platform#{$num}", 'WARN'); $this->api->patch("/repos/{$org}/MokoCLI/issues/{$num}", $patch);
$this->log("🚨 Failure issue #{$num} updated: {$org}/MokoCLI#{$num}", 'WARN');
} else { } else {
$issue = $this->api->post("/repos/{$org}/moko-platform/issues", [ $issue = $this->api->post("/repos/{$org}/MokoCLI/issues", [
=======
$this->api->patch("/repos/{$org}/mokoplatform/issues/{$num}", $patch);
$this->log("🚨 Failure issue #{$num} updated: {$org}/mokoplatform#{$num}", 'WARN');
} else {
$issue = $this->api->post("/repos/{$org}/mokoplatform/issues", [
>>>>>>> main
'title' => $title, 'title' => $title,
'body' => $body, 'body' => $body,
'labels' => ['push-failure'], 'labels' => ['push-failure'],
'assignees' => ['jmiller'], 'assignees' => ['jmiller'],
]); ]);
$num = $issue['number'] ?? '?'; $num = $issue['number'] ?? '?';
$this->log("🚨 Failure issue created: {$org}/moko-platform#{$num}", 'WARN'); <<<<<<< HEAD
$this->log("🚨 Failure issue created: {$org}/MokoCLI#{$num}", 'WARN');
=======
$this->log("🚨 Failure issue created: {$org}/mokoplatform#{$num}", 'WARN');
>>>>>>> main
} }
} catch (\Exception $e) { } catch (\Exception $e) {
$this->log("⚠️ Could not create/update failure issue: " . $e->getMessage(), 'WARN'); $this->log("⚠️ Could not create/update failure issue: " . $e->getMessage(), 'WARN');
@@ -637,14 +699,23 @@ class PushFiles extends CliFramework
private function buildPRBody(array $entries): string private function buildPRBody(array $entries): string
{ {
$now = gmdate('Y-m-d H:i:s') . ' UTC'; $now = gmdate('Y-m-d H:i:s') . ' UTC';
$lines = ["## moko-platform File Push\n", "**Pushed:** {$now}\n", '### Files\n']; <<<<<<< HEAD
$lines = ["## MokoCLI File Push\n", "**Pushed:** {$now}\n", '### Files\n'];
=======
$lines = ["## mokoplatform File Push\n", "**Pushed:** {$now}\n", '### Files\n'];
>>>>>>> main
foreach ($entries as $entry) { foreach ($entries as $entry) {
$lines[] = "- `{$entry['destination']}`"; $lines[] = "- `{$entry['destination']}`";
} }
$sourceUrl = $this->adapter->getRepoWebUrl(self::DEFAULT_ORG, 'moko-platform'); <<<<<<< HEAD
$lines[] = "\n---\n*Generated by [moko-platform]({$sourceUrl}) `push_files.php`*"; $sourceUrl = $this->adapter->getRepoWebUrl(self::DEFAULT_ORG, 'MokoCLI');
$lines[] = "\n---\n*Generated by [MokoCLI]({$sourceUrl}) `push_files.php`*";
=======
$sourceUrl = $this->adapter->getRepoWebUrl(self::DEFAULT_ORG, 'mokoplatform');
$lines[] = "\n---\n*Generated by [mokoplatform]({$sourceUrl}) `push_files.php`*";
>>>>>>> main
return implode("\n", $lines); return implode("\n", $lines);
} }
+22 -8
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
<<<<<<< HEAD
* DEFGROUP: MokoCLI.Automation
* INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: MokoPlatform.Automation * DEFGROUP: MokoPlatform.Automation
* INGROUP: MokoPlatform * INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /automation/push_manifest_xml.php * PATH: /automation/push_manifest_xml.php
* BRIEF: Push XML manifests to all governed repositories * BRIEF: Push XML manifests to all governed repositories
*/ */
@@ -18,8 +24,8 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
use MokoEnterprise\MokoStandardsParser; use MokoCli\ManifestParser;
class PushManifestXmlCli extends CliFramework class PushManifestXmlCli extends CliFramework
{ {
@@ -44,10 +50,14 @@ class PushManifestXmlCli extends CliFramework
$skipStr = $this->getArgument('--skip'); $skipStr = $this->getArgument('--skip');
$skipRepos = $skipStr !== '' ? array_map('trim', explode(',', $skipStr)) : []; $skipRepos = $skipStr !== '' ? array_map('trim', explode(',', $skipStr)) : [];
$parser = new MokoStandardsParser(); $parser = new ManifestParser();
$tmpBase = sys_get_temp_dir() . '/moko-manifest-push-' . getmypid(); $tmpBase = sys_get_temp_dir() . '/moko-manifest-push-' . getmypid();
echo "=== moko-platform XML Manifest Push ===\n"; <<<<<<< HEAD
echo "=== MokoCLI XML Manifest Push ===\n";
=======
echo "=== mokoplatform XML Manifest Push ===\n";
>>>>>>> main
echo "Org: {$giteaOrg}\n"; echo "Org: {$giteaOrg}\n";
echo "Mode: " . ($this->dryRun ? "DRY RUN" : "LIVE") . "\n"; echo "Mode: " . ($this->dryRun ? "DRY RUN" : "LIVE") . "\n";
if ($repoFilter) { if ($repoFilter) {
@@ -97,8 +107,8 @@ class PushManifestXmlCli extends CliFramework
'description' => $repo['description'] ?? '', 'description' => $repo['description'] ?? '',
'license' => 'GPL-3.0-or-later', 'license' => 'GPL-3.0-or-later',
'topics' => $repo['topics'] ?? [], 'topics' => $repo['topics'] ?? [],
'language' => $repo['language'] ?? MokoStandardsParser::platformLanguage($platform), 'language' => $repo['language'] ?? ManifestParser::platformLanguage($platform),
'package_type' => MokoStandardsParser::platformPackageType($platform), 'package_type' => ManifestParser::platformPackageType($platform),
'last_synced' => date('c'), 'last_synced' => date('c'),
]); ]);
@@ -125,7 +135,11 @@ class PushManifestXmlCli extends CliFramework
// Check if already XML and up-to-date // Check if already XML and up-to-date
$manifestPath = "{$workDir}/.mokogitea/manifest.xml"; $manifestPath = "{$workDir}/.mokogitea/manifest.xml";
$existingIsXml = file_exists($manifestPath) && str_contains(file_get_contents($manifestPath), '<moko-platform'); <<<<<<< HEAD
$existingIsXml = file_exists($manifestPath) && str_contains(file_get_contents($manifestPath), '<MokoCLI');
=======
$existingIsXml = file_exists($manifestPath) && str_contains(file_get_contents($manifestPath), '<mokoplatform');
>>>>>>> main
if ($existingIsXml && !$force) { if ($existingIsXml && !$force) {
$existingPlatform = $parser->extractPlatform(file_get_contents($manifestPath)); $existingPlatform = $parser->extractPlatform(file_get_contents($manifestPath));
if ($existingPlatform === $platform) { if ($existingPlatform === $platform) {
+22 -8
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
<<<<<<< HEAD
* DEFGROUP: MokoCLI.Automation
* INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: MokoPlatform.Automation * DEFGROUP: MokoPlatform.Automation
* INGROUP: MokoPlatform * INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /automation/push_mokostandards_xml.php * PATH: /automation/push_mokostandards_xml.php
* BRIEF: Push XML manifests to all governed repositories * BRIEF: Push XML manifests to all governed repositories
*/ */
@@ -18,8 +24,8 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
use MokoEnterprise\MokoStandardsParser; use MokoCli\ManifestParser;
class PushMokostandardsXmlCli extends CliFramework class PushMokostandardsXmlCli extends CliFramework
{ {
@@ -44,10 +50,14 @@ class PushMokostandardsXmlCli extends CliFramework
$skipStr = $this->getArgument('--skip'); $skipStr = $this->getArgument('--skip');
$skipRepos = $skipStr !== '' ? array_map('trim', explode(',', $skipStr)) : []; $skipRepos = $skipStr !== '' ? array_map('trim', explode(',', $skipStr)) : [];
$parser = new MokoStandardsParser(); $parser = new ManifestParser();
$tmpBase = sys_get_temp_dir() . '/moko-manifest-push-' . getmypid(); $tmpBase = sys_get_temp_dir() . '/moko-manifest-push-' . getmypid();
echo "=== moko-platform XML Manifest Push ===\n"; <<<<<<< HEAD
echo "=== MokoCLI XML Manifest Push ===\n";
=======
echo "=== mokoplatform XML Manifest Push ===\n";
>>>>>>> main
echo "Org: {$giteaOrg}\n"; echo "Org: {$giteaOrg}\n";
echo "Mode: " . ($this->dryRun ? "DRY RUN" : "LIVE") . "\n"; echo "Mode: " . ($this->dryRun ? "DRY RUN" : "LIVE") . "\n";
if ($repoFilter) { if ($repoFilter) {
@@ -97,8 +107,8 @@ class PushMokostandardsXmlCli extends CliFramework
'description' => $repo['description'] ?? '', 'description' => $repo['description'] ?? '',
'license' => 'GPL-3.0-or-later', 'license' => 'GPL-3.0-or-later',
'topics' => $repo['topics'] ?? [], 'topics' => $repo['topics'] ?? [],
'language' => $repo['language'] ?? MokoStandardsParser::platformLanguage($platform), 'language' => $repo['language'] ?? ManifestParser::platformLanguage($platform),
'package_type' => MokoStandardsParser::platformPackageType($platform), 'package_type' => ManifestParser::platformPackageType($platform),
'last_synced' => date('c'), 'last_synced' => date('c'),
]); ]);
@@ -125,7 +135,11 @@ class PushMokostandardsXmlCli extends CliFramework
// Check if already XML and up-to-date // Check if already XML and up-to-date
$manifestPath = "{$workDir}/.mokogitea/manifest.xml"; $manifestPath = "{$workDir}/.mokogitea/manifest.xml";
$existingIsXml = file_exists($manifestPath) && str_contains(file_get_contents($manifestPath), '<moko-platform'); <<<<<<< HEAD
$existingIsXml = file_exists($manifestPath) && str_contains(file_get_contents($manifestPath), '<MokoCLI');
=======
$existingIsXml = file_exists($manifestPath) && str_contains(file_get_contents($manifestPath), '<mokoplatform');
>>>>>>> main
if ($existingIsXml && !$force) { if ($existingIsXml && !$force) {
$existingPlatform = $parser->extractPlatform(file_get_contents($manifestPath)); $existingPlatform = $parser->extractPlatform(file_get_contents($manifestPath));
if ($existingPlatform === $platform) { if ($existingPlatform === $platform) {
+43 -10
View File
@@ -9,9 +9,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
<<<<<<< HEAD
* DEFGROUP: MokoCLI.Automation
* INGROUP: MokoCLI.Scripts
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: MokoPlatform.Automation * DEFGROUP: MokoPlatform.Automation
* INGROUP: MokoPlatform.Scripts * INGROUP: MokoPlatform.Scripts
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /automation/repo_cleanup.php * PATH: /automation/repo_cleanup.php
* BRIEF: Enterprise repository cleanup — branches, PRs, issues, workflows, labels, logs * BRIEF: Enterprise repository cleanup — branches, PRs, issues, workflows, labels, logs
*/ */
@@ -21,7 +27,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\{ApiClient, AuditLogger, CliFramework, Config, GitPlatformAdapter, MetricsCollector, PlatformAdapterFactory}; use MokoCli\{ApiClient, AuditLogger, CliFramework, Config, GitPlatformAdapter, MetricsCollector, PlatformAdapterFactory};
/** /**
* Enterprise Repository Cleanup * Enterprise Repository Cleanup
@@ -39,14 +45,23 @@ use MokoEnterprise\{ApiClient, AuditLogger, CliFramework, Config, GitPlatformAda
class RepoCleanup extends CliFramework class RepoCleanup extends CliFramework
{ {
private const VERSION = '09.23.00'; private const VERSION = '09.23.00';
private const SYNC_PREFIX = 'chore/sync-moko-platform-'; <<<<<<< HEAD
private const CURRENT_BRANCH = 'chore/sync-moko-platform-v04.02.00'; private const SYNC_PREFIX = 'chore/sync-MokoCLI-';
private const CURRENT_BRANCH = 'chore/sync-MokoCLI-v04.02.00';
=======
private const SYNC_PREFIX = 'chore/sync-mokoplatform-';
private const CURRENT_BRANCH = 'chore/sync-mokoplatform-v04.02.00';
>>>>>>> main
/** Workflow files that have been retired and should be deleted from governed repos. */ /** Workflow files that have been retired and should be deleted from governed repos. */
private const RETIRED_WORKFLOWS = [ private const RETIRED_WORKFLOWS = [
'build.yml', 'code-quality.yml', 'release-cycle.yml', 'release-pipeline.yml', 'build.yml', 'code-quality.yml', 'release-cycle.yml', 'release-pipeline.yml',
'branch-cleanup.yml', 'auto-update-changelog.yml', 'enterprise-issue-manager.yml', 'branch-cleanup.yml', 'auto-update-changelog.yml', 'enterprise-issue-manager.yml',
'flush-actions-cache.yml', 'moko-platform-script-runner.yml', 'unified-ci.yml', <<<<<<< HEAD
'flush-actions-cache.yml', 'MokoCLI-script-runner.yml', 'unified-ci.yml',
=======
'flush-actions-cache.yml', 'mokoplatform-script-runner.yml', 'unified-ci.yml',
>>>>>>> main
'unified-platform-testing.yml', 'reusable-build.yml', 'reusable-ci-validation.yml', 'unified-platform-testing.yml', 'reusable-build.yml', 'reusable-ci-validation.yml',
'reusable-deploy.yml', 'reusable-php-quality.yml', 'reusable-platform-testing.yml', 'reusable-deploy.yml', 'reusable-php-quality.yml', 'reusable-platform-testing.yml',
'reusable-project-detector.yml', 'reusable-release.yml', 'reusable-script-executor.yml', 'reusable-project-detector.yml', 'reusable-release.yml', 'reusable-script-executor.yml',
@@ -98,7 +113,11 @@ class RepoCleanup extends CliFramework
} }
$this->logMsg("🧹 moko-platform Repository Cleanup v" . self::VERSION); <<<<<<< HEAD
$this->logMsg("🧹 MokoCLI Repository Cleanup v" . self::VERSION);
=======
$this->logMsg("🧹 mokoplatform Repository Cleanup v" . self::VERSION);
>>>>>>> main
$this->logMsg("Organization: {$org}"); $this->logMsg("Organization: {$org}");
$this->logMsg("Current sync branch: " . self::CURRENT_BRANCH); $this->logMsg("Current sync branch: " . self::CURRENT_BRANCH);
if ($this->dryRun) { if ($this->dryRun) {
@@ -225,7 +244,11 @@ class RepoCleanup extends CliFramework
} }
$allRepos = $this->adapter->listOrgRepos($org, $skipArchived); $allRepos = $this->adapter->listOrgRepos($org, $skipArchived);
return array_filter($allRepos, fn($r) => !in_array($r['name'], ['moko-platform', '.github-private'], true)); <<<<<<< HEAD
return array_filter($allRepos, fn($r) => !in_array($r['name'], ['MokoCLI', '.github-private'], true));
=======
return array_filter($allRepos, fn($r) => !in_array($r['name'], ['mokoplatform', '.github-private'], true));
>>>>>>> main
} }
// ─── Cleanup operations ────────────────────────────────────────────── // ─── Cleanup operations ──────────────────────────────────────────────
@@ -463,9 +486,15 @@ class RepoCleanup extends CliFramework
private function checkLabels(string $org, string $repo, array &$results): void private function checkLabels(string $org, string $repo, array &$results): void
{ {
try { try {
$this->api->get("/repos/{$org}/{$repo}/labels/moko-platform"); <<<<<<< HEAD
$this->api->get("/repos/{$org}/{$repo}/labels/MokoCLI");
} catch (\Exception $e) { } catch (\Exception $e) {
$this->logMsg(" ⚠️ Missing 'moko-platform' label"); $this->logMsg(" ⚠️ Missing 'MokoCLI' label");
=======
$this->api->get("/repos/{$org}/{$repo}/labels/mokoplatform");
} catch (\Exception $e) {
$this->logMsg(" ⚠️ Missing 'mokoplatform' label");
>>>>>>> main
$results['labels_missing']++; $results['labels_missing']++;
$this->api->resetCircuitBreaker(); $this->api->resetCircuitBreaker();
} }
@@ -479,7 +508,11 @@ class RepoCleanup extends CliFramework
if (preg_match('/^\s*VERSION:\s*(\d{2}\.\d{2}\.\d{2})/m', $content, $m)) { if (preg_match('/^\s*VERSION:\s*(\d{2}\.\d{2}\.\d{2})/m', $content, $m)) {
$version = $m[1]; $version = $m[1];
// Check manifest.xml for the tracked moko-platform version <<<<<<< HEAD
// Check manifest.xml for the tracked MokoCLI version
=======
// Check manifest.xml for the tracked mokoplatform version
>>>>>>> main
try { try {
$mokoFile = $this->api->get("/repos/{$org}/{$repo}/contents/.mokogitea/manifest.xml"); $mokoFile = $this->api->get("/repos/{$org}/{$repo}/contents/.mokogitea/manifest.xml");
$mokoContent = base64_decode($mokoFile['content'] ?? ''); $mokoContent = base64_decode($mokoFile['content'] ?? '');
+3 -3
View File
@@ -4,9 +4,9 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech> # Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# #
# DEFGROUP: MokoPlatform.Automation.ServerAutoheal # DEFGROUP: MokoCLI.Automation.ServerAutoheal
# INGROUP: MokoPlatform.Automation # INGROUP: MokoCLI.Automation
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /automation/server-autoheal.sh # PATH: /automation/server-autoheal.sh
# BRIEF: Server auto-heal on unclean restart + split system/content backups # BRIEF: Server auto-heal on unclean restart + split system/content backups
# #
+24 -6
View File
@@ -9,11 +9,19 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: MokoStandards.CLI <<<<<<< HEAD
* INGROUP: MokoStandards * DEFGROUP: MokoCLI.CLI
* INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /bin/moko
* BRIEF: Unified CLI dispatcher — run any MokoCLI script without needing GitHub Actions
=======
* DEFGROUP: MokoCli.CLI
* INGROUP: MokoCli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /bin/moko * PATH: /bin/moko
* BRIEF: Unified CLI dispatcher — run any MokoStandards script without needing GitHub Actions * BRIEF: Unified CLI dispatcher — run any MokoCli script without needing GitHub Actions
>>>>>>> main
* *
* USAGE * USAGE
* php bin/moko <command> [options] (all platforms) * php bin/moko <command> [options] (all platforms)
@@ -220,6 +228,9 @@ const COMMAND_MAP = [
// Licensing // Licensing
'license' => 'cli/license_manage.php', 'license' => 'cli/license_manage.php',
// Security
'security:advisories' => 'security/advisory_scan.php',
// Shell completion // Shell completion
'completion' => 'cli/completion.php', 'completion' => 'cli/completion.php',
@@ -292,10 +303,17 @@ function printHelp(): void
{ {
echo <<<'HELP' echo <<<'HELP'
╔══════════════════════════════════════════════════════════╗ ╔══════════════════════════════════════════════════════════╗
║ MokoStandards CLI (bin/moko) ║ <<<<<<< HEAD
║ MokoCLI (bin/moko) ║
╚══════════════════════════════════════════════════════════╝ ╚══════════════════════════════════════════════════════════╝
Run any MokoStandards script locally without GitHub Actions. Run any MokoCLI script locally without GitHub Actions.
=======
║ MokoCli CLI (bin/moko) ║
╚══════════════════════════════════════════════════════════╝
Run any MokoCli script locally without GitHub Actions.
>>>>>>> main
USAGE USAGE
php bin/moko <command> [options] (all platforms) php bin/moko <command> [options] (all platforms)
@@ -397,7 +415,7 @@ function loadPluginCommands(): array
$commands = []; $commands = [];
foreach (glob("{$pluginDir}/*Plugin.php") as $file) { foreach (glob("{$pluginDir}/*Plugin.php") as $file) {
$className = 'MokoEnterprise\\Plugins\\' $className = 'MokoCli\\Plugins\\'
. pathinfo($file, PATHINFO_FILENAME); . pathinfo($file, PATHINFO_FILENAME);
if (!class_exists($className)) { if (!class_exists($className)) {
+22 -8
View File
@@ -8,9 +8,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/archive_repo.php * PATH: /cli/archive_repo.php
* BRIEF: Gracefully retire a governed repository — archive, close issues/PRs, remove sync def * BRIEF: Gracefully retire a governed repository — archive, close issues/PRs, remove sync def
*/ */
@@ -20,9 +26,9 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
use MokoEnterprise\Config; use MokoCli\Config;
use MokoEnterprise\PlatformAdapterFactory; use MokoCli\PlatformAdapterFactory;
class ArchiveRepoCli extends CliFramework class ArchiveRepoCli extends CliFramework
{ {
@@ -135,7 +141,11 @@ class ArchiveRepoCli extends CliFramework
try { try {
$issue = $adapter->createIssue( $issue = $adapter->createIssue(
$org, $org,
'moko-platform', <<<<<<< HEAD
'MokoCLI',
=======
'mokoplatform',
>>>>>>> main
"chore: archived repository {$repoName}", "chore: archived repository {$repoName}",
"## Repository Archived\n\n" "## Repository Archived\n\n"
. "**Repository:** `{$org}/{$repoName}`\n" . "**Repository:** `{$org}/{$repoName}`\n"
@@ -150,7 +160,11 @@ class ArchiveRepoCli extends CliFramework
] ]
); );
if (isset($issue['number'])) { if (isset($issue['number'])) {
echo " Archival record: moko-platform#{$issue['number']}\n"; <<<<<<< HEAD
echo " Archival record: MokoCLI#{$issue['number']}\n";
=======
echo " Archival record: mokoplatform#{$issue['number']}\n";
>>>>>>> main
} }
} catch (\Exception $e) { } catch (\Exception $e) {
echo " Warning: could not create archival record: " . $e->getMessage() . "\n"; echo " Warning: could not create archival record: " . $e->getMessage() . "\n";
+8 -2
View File
@@ -14,9 +14,15 @@
* (at your option) any later version. * (at your option) any later version.
* *
* FILE INFORMATION * FILE INFORMATION
<<<<<<< HEAD
* DEFGROUP: MokoCLI.Enterprise.CLI
* INGROUP: MokoCLI.Enterprise
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: MokoPlatform.Enterprise.CLI * DEFGROUP: MokoPlatform.Enterprise.CLI
* INGROUP: MokoPlatform.Enterprise * INGROUP: MokoPlatform.Enterprise
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/audit_query.php * PATH: /cli/audit_query.php
* BRIEF: Search, filter, and export audit logs * BRIEF: Search, filter, and export audit logs
*/ */
@@ -25,7 +31,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
/** /**
* CLI tool to search, filter, and export audit logs. * CLI tool to search, filter, and export audit logs.
+10 -4
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/badge_update.php * PATH: /cli/badge_update.php
* BRIEF: Update [VERSION: XX.XX.XX] badges in all markdown files * BRIEF: Update [VERSION: XX.XX.XX] badges in all markdown files
*/ */
@@ -17,7 +23,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class BadgeUpdateCli extends CliFramework class BadgeUpdateCli extends CliFramework
{ {
+13 -5
View File
@@ -6,11 +6,19 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/branch_rename.php * PATH: /cli/branch_rename.php
* VERSION: 09.23.00 * VERSION: 09.25.05
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/branch_rename.php
* VERSION: 09.32.00
>>>>>>> main
* BRIEF: Rename a git branch via Gitea API (create new, update PR, delete old) * BRIEF: Rename a git branch via Gitea API (create new, update PR, delete old)
*/ */
@@ -18,7 +26,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class BranchRenameCli extends CliFramework class BranchRenameCli extends CliFramework
{ {
+23 -7
View File
@@ -8,11 +8,19 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/bulk_workflow_push.php * PATH: /cli/bulk_workflow_push.php
* VERSION: 09.23.00 * VERSION: 09.25.05
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/bulk_workflow_push.php
* VERSION: 09.32.00
>>>>>>> main
* BRIEF: Push a workflow file to all governed repos via the Gitea Contents API * BRIEF: Push a workflow file to all governed repos via the Gitea Contents API
*/ */
@@ -20,7 +28,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class BulkWorkflowPushCli extends CliFramework class BulkWorkflowPushCli extends CliFramework
{ {
@@ -154,7 +162,11 @@ class BulkWorkflowPushCli extends CliFramework
'content' => $encodedContent, 'content' => $encodedContent,
'sha' => $remoteSha, 'sha' => $remoteSha,
'message' => "chore: sync {$destPath} " 'message' => "chore: sync {$destPath} "
. "from moko-platform [skip ci]", <<<<<<< HEAD
. "from MokoCLI [skip ci]",
=======
. "from mokoplatform [skip ci]",
>>>>>>> main
'branch' => $branch, 'branch' => $branch,
]); ]);
@@ -184,7 +196,11 @@ class BulkWorkflowPushCli extends CliFramework
$payload = json_encode([ $payload = json_encode([
'content' => $encodedContent, 'content' => $encodedContent,
'message' => "chore: add {$destPath} " 'message' => "chore: add {$destPath} "
. "from moko-platform [skip ci]", <<<<<<< HEAD
. "from MokoCLI [skip ci]",
=======
. "from mokoplatform [skip ci]",
>>>>>>> main
'branch' => $branch, 'branch' => $branch,
]); ]);
+13 -5
View File
@@ -8,11 +8,19 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/bulk_workflow_trigger.php * PATH: /cli/bulk_workflow_trigger.php
* VERSION: 09.23.00 * VERSION: 09.25.05
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/bulk_workflow_trigger.php
* VERSION: 09.32.00
>>>>>>> main
* BRIEF: Trigger a workflow across multiple repos at once * BRIEF: Trigger a workflow across multiple repos at once
*/ */
@@ -20,7 +28,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class BulkWorkflowTriggerCli extends CliFramework class BulkWorkflowTriggerCli extends CliFramework
{ {
+10 -4
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/changelog_promote.php * PATH: /cli/changelog_promote.php
* BRIEF: Promote [Unreleased] section in CHANGELOG.md to a versioned entry * BRIEF: Promote [Unreleased] section in CHANGELOG.md to a versioned entry
*/ */
@@ -17,7 +23,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class ChangelogPromoteCli extends CliFramework class ChangelogPromoteCli extends CliFramework
{ {
+10 -4
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/changelog_prune.php * PATH: /cli/changelog_prune.php
* BRIEF: Prune old CHANGELOG.md entries — keeps [Unreleased] + last N releases * BRIEF: Prune old CHANGELOG.md entries — keeps [Unreleased] + last N releases
*/ */
@@ -17,7 +23,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class ChangelogPruneCli extends CliFramework class ChangelogPruneCli extends CliFramework
{ {
+13 -5
View File
@@ -8,11 +8,19 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/client_dashboard.php * PATH: /cli/client_dashboard.php
* VERSION: 09.23.00 * VERSION: 09.25.05
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/client_dashboard.php
* VERSION: 09.32.00
>>>>>>> main
* BRIEF: Generate unified client dashboard HTML * BRIEF: Generate unified client dashboard HTML
*/ */
@@ -20,7 +28,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class ClientDashboardCli extends CliFramework class ClientDashboardCli extends CliFramework
{ {
+10 -4
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/client_health_check.php * PATH: /cli/client_health_check.php
* BRIEF: Verify a client site's update server, installed version, and release availability * BRIEF: Verify a client site's update server, installed version, and release availability
*/ */
@@ -17,7 +23,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class ClientHealthCheckCli extends CliFramework class ClientHealthCheckCli extends CliFramework
{ {
+13 -5
View File
@@ -8,11 +8,19 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/client_inventory.php * PATH: /cli/client_inventory.php
* VERSION: 09.23.00 * VERSION: 09.25.05
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/client_inventory.php
* VERSION: 09.32.00
>>>>>>> main
* BRIEF: Discover and list all client-waas repos with their server configuration status * BRIEF: Discover and list all client-waas repos with their server configuration status
*/ */
@@ -20,7 +28,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class ClientInventoryCli extends CliFramework class ClientInventoryCli extends CliFramework
{ {
+13 -5
View File
@@ -8,11 +8,19 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/client_provision.php * PATH: /cli/client_provision.php
* VERSION: 09.23.00 * VERSION: 09.25.05
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/client_provision.php
* VERSION: 09.32.00
>>>>>>> main
* BRIEF: Provision a new client environment end-to-end * BRIEF: Provision a new client environment end-to-end
*/ */
@@ -20,7 +28,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class ClientProvisionCli extends CliFramework class ClientProvisionCli extends CliFramework
{ {
+10 -4
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/completion.php * PATH: /cli/completion.php
* BRIEF: Generate bash/zsh tab completion scripts for bin/moko * BRIEF: Generate bash/zsh tab completion scripts for bin/moko
*/ */
@@ -17,7 +23,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class CompletionCli extends CliFramework class CompletionCli extends CliFramework
{ {
+34 -12
View File
@@ -8,9 +8,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/create_project.php * PATH: /cli/create_project.php
* BRIEF: Create baseline GitHub Projects for repositories with standard fields and views * BRIEF: Create baseline GitHub Projects for repositories with standard fields and views
*/ */
@@ -19,12 +25,16 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class CreateProjectCli extends CliFramework class CreateProjectCli extends CliFramework
{ {
/** @var string[] */ /** @var string[] */
private array $ALWAYS_EXCLUDE = ['moko-platform', '.github-private']; <<<<<<< HEAD
private array $ALWAYS_EXCLUDE = ['MokoCLI', '.github-private'];
=======
private array $ALWAYS_EXCLUDE = ['mokoplatform', '.github-private'];
>>>>>>> main
/** @var array<string, string> */ /** @var array<string, string> */
private array $PLATFORM_TO_TYPE = [ private array $PLATFORM_TO_TYPE = [
@@ -80,10 +90,10 @@ class CreateProjectCli extends CliFramework
return 2; return 2;
} }
$config = \MokoEnterprise\Config::load(); $config = \MokoCli\Config::load();
$platformName = $config->getString('platform', 'gitea'); $platformName = $config->getString('platform', 'gitea');
try { try {
$adapter = \MokoEnterprise\PlatformAdapterFactory::create($config); $adapter = \MokoCli\PlatformAdapterFactory::create($config);
$api = $adapter->getApiClient(); $api = $adapter->getApiClient();
} catch (\Exception $e) { } catch (\Exception $e) {
$this->log('ERROR', "Platform initialization failed: " . $e->getMessage()); $this->log('ERROR', "Platform initialization failed: " . $e->getMessage());
@@ -183,7 +193,11 @@ class CreateProjectCli extends CliFramework
CURLOPT_HTTPHEADER => [ CURLOPT_HTTPHEADER => [
'Authorization: bearer ' . $token, 'Authorization: bearer ' . $token,
'Content-Type: application/json', 'Content-Type: application/json',
'User-Agent: moko-platform-CreateProject', <<<<<<< HEAD
'User-Agent: MokoCLI-CreateProject',
=======
'User-Agent: mokoplatform-CreateProject',
>>>>>>> main
], ],
]); ]);
$body = (string) curl_exec($ch); $body = (string) curl_exec($ch);
@@ -205,7 +219,7 @@ class CreateProjectCli extends CliFramework
return $data['data'] ?? []; return $data['data'] ?? [];
} }
private function restGet(string $path, string $token, ?\MokoEnterprise\ApiClient $apiClient = null): array private function restGet(string $path, string $token, ?\MokoCli\ApiClient $apiClient = null): array
{ {
if ($apiClient !== null) { if ($apiClient !== null) {
try { try {
@@ -217,7 +231,7 @@ class CreateProjectCli extends CliFramework
return []; return [];
} }
private function detectRepoPlatform(string $org, string $repo, string $token, ?\MokoEnterprise\ApiClient $apiClient = null): string private function detectRepoPlatform(string $org, string $repo, string $token, ?\MokoCli\ApiClient $apiClient = null): string
{ {
foreach (['.github/.mokostandards', '.mokogitea/.mokostandards', '.mokostandards'] as $path) { foreach (['.github/.mokostandards', '.mokogitea/.mokostandards', '.mokostandards'] as $path) {
$data = $this->restGet("repos/{$org}/{$repo}/contents/{$path}", $token, $apiClient); $data = $this->restGet("repos/{$org}/{$repo}/contents/{$path}", $token, $apiClient);
@@ -422,14 +436,22 @@ class CreateProjectCli extends CliFramework
updateProjectV2(input: { updateProjectV2(input: {
projectId: $projectId, projectId: $projectId,
shortDescription: $shortDescription, shortDescription: $shortDescription,
readme: "Managed by moko-platform. Run `php cli/create_project.php` to regenerate." <<<<<<< HEAD
readme: "Managed by MokoCLI. Run `php cli/create_project.php` to regenerate."
=======
readme: "Managed by mokoplatform. Run `php cli/create_project.php` to regenerate."
>>>>>>> main
}) { }) {
projectV2 { id } projectV2 { id }
} }
}', }',
[ [
'projectId' => $projectId, 'projectId' => $projectId,
'shortDescription' => "Standard project board for {$repo}. Auto-created by moko-platform.", <<<<<<< HEAD
'shortDescription' => "Standard project board for {$repo}. Auto-created by MokoCLI.",
=======
'shortDescription' => "Standard project board for {$repo}. Auto-created by mokoplatform.",
>>>>>>> main
], ],
$token $token
); );
+62 -21
View File
@@ -8,11 +8,19 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/create_repo.php * PATH: /cli/create_repo.php
* BRIEF: Scaffold a new governed repository with full moko-platform baseline * BRIEF: Scaffold a new governed repository with full MokoCLI baseline
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/create_repo.php
* BRIEF: Scaffold a new governed repository with full mokoplatform baseline
>>>>>>> main
*/ */
declare(strict_types=1); declare(strict_types=1);
@@ -20,15 +28,19 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
use MokoEnterprise\Config; use MokoCli\Config;
use MokoEnterprise\PlatformAdapterFactory; use MokoCli\PlatformAdapterFactory;
class CreateRepoCli extends CliFramework class CreateRepoCli extends CliFramework
{ {
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Scaffold a new governed repository with full moko-platform baseline'); <<<<<<< HEAD
$this->setDescription('Scaffold a new governed repository with full MokoCLI baseline');
=======
$this->setDescription('Scaffold a new governed repository with full mokoplatform baseline');
>>>>>>> main
$this->addArgument('--name', 'Repository name', null); $this->addArgument('--name', 'Repository name', null);
$this->addArgument('--type', 'Project type', null); $this->addArgument('--type', 'Project type', null);
$this->addArgument('--description', 'Repository description', ''); $this->addArgument('--description', 'Repository description', '');
@@ -60,16 +72,29 @@ class CreateRepoCli extends CliFramework
'generic' => 'generic', 'generic' => 'generic',
]; ];
$TYPE_TO_TOPICS = [ $TYPE_TO_TOPICS = [
'dolibarr' => ['dolibarr', 'erp', 'crm', 'php', 'moko-platform'], <<<<<<< HEAD
'joomla' => ['joomla', 'cms', 'php', 'moko-platform'], 'dolibarr' => ['dolibarr', 'erp', 'crm', 'php', 'MokoCLI'],
'nodejs' => ['nodejs', 'javascript', 'typescript', 'moko-platform'], 'joomla' => ['joomla', 'cms', 'php', 'MokoCLI'],
'terraform' => ['terraform', 'infrastructure', 'iac', 'moko-platform'], 'nodejs' => ['nodejs', 'javascript', 'typescript', 'MokoCLI'],
'python' => ['python', 'moko-platform'], 'terraform' => ['terraform', 'infrastructure', 'iac', 'MokoCLI'],
'wordpress' => ['wordpress', 'php', 'cms', 'moko-platform'], 'python' => ['python', 'MokoCLI'],
'generic' => ['moko-platform'], 'wordpress' => ['wordpress', 'php', 'cms', 'MokoCLI'],
'generic' => ['MokoCLI'],
]; ];
$platform = $TYPE_TO_PLATFORM[$type] ?? 'generic'; $platform = $TYPE_TO_PLATFORM[$type] ?? 'generic';
$topics = $TYPE_TO_TOPICS[$type] ?? ['moko-platform']; $topics = $TYPE_TO_TOPICS[$type] ?? ['MokoCLI'];
=======
'dolibarr' => ['dolibarr', 'erp', 'crm', 'php', 'mokoplatform'],
'joomla' => ['joomla', 'cms', 'php', 'mokoplatform'],
'nodejs' => ['nodejs', 'javascript', 'typescript', 'mokoplatform'],
'terraform' => ['terraform', 'infrastructure', 'iac', 'mokoplatform'],
'python' => ['python', 'mokoplatform'],
'wordpress' => ['wordpress', 'php', 'cms', 'mokoplatform'],
'generic' => ['mokoplatform'],
];
$platform = $TYPE_TO_PLATFORM[$type] ?? 'generic';
$topics = $TYPE_TO_TOPICS[$type] ?? ['mokoplatform'];
>>>>>>> main
$platformName = $adapter->getPlatformName(); $platformName = $adapter->getPlatformName();
$vis = $private ? 'private' : 'public'; $vis = $private ? 'private' : 'public';
echo "Scaffolding new repository: {$org}/{$name}" echo "Scaffolding new repository: {$org}/{$name}"
@@ -84,7 +109,11 @@ class CreateRepoCli extends CliFramework
if (!$this->dryRun) { if (!$this->dryRun) {
try { try {
$data = $adapter->createOrgRepo($org, $name, [ $data = $adapter->createOrgRepo($org, $name, [
'description' => $description ?: "Managed by moko-platform ({$type})", <<<<<<< HEAD
'description' => $description ?: "Managed by MokoCLI ({$type})",
=======
'description' => $description ?: "Managed by mokoplatform ({$type})",
>>>>>>> main
'private' => $private, 'private' => $private,
'has_issues' => true, 'has_issues' => true,
'has_projects' => true, 'has_projects' => true,
@@ -138,12 +167,16 @@ class CreateRepoCli extends CliFramework
echo "Step 4: Creating README.md...\n"; echo "Step 4: Creating README.md...\n";
$baseUrl = $platformName === 'gitea' ? $config->getString('gitea.url', 'https://git.mokoconsulting.tech') : 'https://github.com'; $baseUrl = $platformName === 'gitea' ? $config->getString('gitea.url', 'https://git.mokoconsulting.tech') : 'https://github.com';
$repoUrl = "{$baseUrl}/{$org}/{$name}"; $repoUrl = "{$baseUrl}/{$org}/{$name}";
$standardsUrl = "{$baseUrl}/{$org}/MokoStandards"; $standardsUrl = "{$baseUrl}/{$org}/MokoCli";
$readmeContent = "<!--\n" $readmeContent = "<!--\n"
. "Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>\n" . "Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>\n"
. "SPDX-License-Identifier: GPL-3.0-or-later\n" . "SPDX-License-Identifier: GPL-3.0-or-later\n"
. "DEFGROUP: {$name}\n" . "DEFGROUP: {$name}\n"
. "INGROUP: moko-platform\n" <<<<<<< HEAD
. "INGROUP: MokoCLI\n"
=======
. "INGROUP: mokoplatform\n"
>>>>>>> main
. "REPO: {$repoUrl}\n" . "REPO: {$repoUrl}\n"
. "PATH: /README.md\n" . "PATH: /README.md\n"
. "BRIEF: {$description}\n" . "BRIEF: {$description}\n"
@@ -152,7 +185,11 @@ class CreateRepoCli extends CliFramework
. "{$description}\n\n" . "{$description}\n\n"
. "## Getting Started\n\n" . "## Getting Started\n\n"
. "This repository is governed by" . "This repository is governed by"
. " [moko-platform]({$standardsUrl}).\n\n" <<<<<<< HEAD
. " [MokoCLI]({$standardsUrl}).\n\n"
=======
. " [mokoplatform]({$standardsUrl}).\n\n"
>>>>>>> main
. "## License\n\n" . "## License\n\n"
. "GPL-3.0-or-later. See [LICENSE](LICENSE)" . "GPL-3.0-or-later. See [LICENSE](LICENSE)"
. " for details.\n"; . " for details.\n";
@@ -169,7 +206,11 @@ class CreateRepoCli extends CliFramework
$name, $name,
'README.md', 'README.md',
$readmeContent, $readmeContent,
'docs: initialize README with moko-platform header [skip ci]', <<<<<<< HEAD
'docs: initialize README with MokoCLI header [skip ci]',
=======
'docs: initialize README with mokoplatform header [skip ci]',
>>>>>>> main
$sha $sha
); );
echo " README.md created\n"; echo " README.md created\n";
+13 -7
View File
@@ -8,9 +8,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
<<<<<<< HEAD
* DEFGROUP: MokoCLI.CLI
* INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: MokoPlatform.CLI * DEFGROUP: MokoPlatform.CLI
* INGROUP: MokoPlatform * INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/deploy_joomla.php * PATH: /cli/deploy_joomla.php
* BRIEF: Smart Joomla deploy — routes files to correct server directories by extension type * BRIEF: Smart Joomla deploy — routes files to correct server directories by extension type
* *
@@ -31,7 +37,7 @@ require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\{CliFramework, SourceResolver};
use phpseclib3\Net\SFTP; use phpseclib3\Net\SFTP;
use phpseclib3\Crypt\PublicKeyLoader; use phpseclib3\Crypt\PublicKeyLoader;
@@ -866,11 +872,11 @@ class DeployJoomla extends CliFramework
} }
} }
// 3-5. Fallback chain // 3-5. Fallback chain (source/ → src/ → htdocs/)
foreach (['src', 'htdocs'] as $candidate) { $resolved = SourceResolver::resolveAbsolute($repoPath);
if (is_dir("{$repoPath}/{$candidate}")) { if ($resolved !== null) {
return "{$repoPath}/{$candidate}"; SourceResolver::warnIfLegacy($repoPath);
} return $resolved;
} }
// Last resort: repo root itself // Last resort: repo root itself
+10 -4
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/dev_branch_reset.php * PATH: /cli/dev_branch_reset.php
* BRIEF: Delete and recreate dev branch from main via Gitea API * BRIEF: Delete and recreate dev branch from main via Gitea API
*/ */
@@ -17,7 +23,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class DevBranchResetCli extends CliFramework class DevBranchResetCli extends CliFramework
{ {
+13 -5
View File
@@ -8,11 +8,19 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/grafana_dashboard.php * PATH: /cli/grafana_dashboard.php
* VERSION: 09.23.00 * VERSION: 09.25.05
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/grafana_dashboard.php
* VERSION: 09.32.00
>>>>>>> main
* BRIEF: Manage Grafana dashboards via API * BRIEF: Manage Grafana dashboards via API
*/ */
@@ -20,7 +28,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class GrafanaDashboardCli extends CliFramework class GrafanaDashboardCli extends CliFramework
{ {
+16 -13
View File
@@ -6,11 +6,19 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/joomla_build.php * PATH: /cli/joomla_build.php
* VERSION: 09.23.00 * VERSION: 09.25.05
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/joomla_build.php
* VERSION: 09.32.00
>>>>>>> main
* BRIEF: Build a Joomla extension ZIP from manifest — all types supported * BRIEF: Build a Joomla extension ZIP from manifest — all types supported
* NOTE: Called by pre-release and auto-release workflows. * NOTE: Called by pre-release and auto-release workflows.
*/ */
@@ -19,7 +27,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\{CliFramework, SourceResolver};
class JoomlaBuildCli extends CliFramework class JoomlaBuildCli extends CliFramework
{ {
@@ -49,17 +57,12 @@ class JoomlaBuildCli extends CliFramework
$path = realpath($path) ?: $path; $path = realpath($path) ?: $path;
// ── Find source directory ────────────────────────────────────────────── // ── Find source directory ──────────────────────────────────────────────
$srcDir = null; $srcDir = SourceResolver::resolveAbsolute($path);
foreach (['src', 'htdocs'] as $d) {
if (is_dir("{$path}/{$d}")) {
$srcDir = "{$path}/{$d}";
break;
}
}
if ($srcDir === null) { if ($srcDir === null) {
$this->log('ERROR', "::error::No src/ or htdocs/ directory in {$path}"); $this->log('ERROR', "::error::No source/ or src/ directory in {$path}");
return 1; return 1;
} }
SourceResolver::warnIfLegacy($path);
// ── Find manifest ────────────────────────────────────────────────────── // ── Find manifest ──────────────────────────────────────────────────────
$manifest = $this->findManifest($srcDir); $manifest = $this->findManifest($srcDir);
+10 -4
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/joomla_compat_check.php * PATH: /cli/joomla_compat_check.php
* BRIEF: Check if extension targetplatform regex matches the latest Joomla version * BRIEF: Check if extension targetplatform regex matches the latest Joomla version
*/ */
@@ -17,7 +23,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class JoomlaCompatCheckCli extends CliFramework class JoomlaCompatCheckCli extends CliFramework
{ {
+550
View File
@@ -0,0 +1,550 @@
#!/usr/bin/env php
<?php
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* FILE INFORMATION
<<<<<<< HEAD
* DEFGROUP: MokoCLI.CLI
* INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/joomla_metadata_validate.php
* VERSION: 09.25.05
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/joomla_metadata_validate.php
* VERSION: 09.32.00
>>>>>>> main
* BRIEF: Validate MokoGitea repo metadata against Joomla extension manifest XML
*/
declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
<<<<<<< HEAD
use MokoEnterprise\CliFramework;
=======
use MokoCli\CliFramework;
>>>>>>> main
class JoomlaMetadataValidateCli extends CliFramework
{
/** Joomla element prefix map — must match MokoGitea's cleanJoomlaElement() */
private const JOOMLA_PREFIX = [
'package' => 'pkg_',
'component' => 'com_',
'module' => 'mod_',
'template' => 'tpl_',
'library' => 'lib_',
'file' => 'file_',
];
protected function configure(): void
{
$this->setDescription('Validate MokoGitea repo metadata against Joomla extension manifest XML');
$this->addArgument('--path', 'Repo root path (default: current directory)', '.');
$this->addArgument('--token', 'Gitea API token (or GITEA_TOKEN env)', '');
$this->addArgument('--org', 'Gitea org', 'MokoConsulting');
$this->addArgument('--repo', 'Repo name (auto-detected from git if empty)', '');
$this->addArgument('--api-base', 'Gitea API base URL', 'https://git.mokoconsulting.tech/api/v1');
$this->addArgument('--ci', 'CI mode: exit 1 on any error', false);
$this->addArgument('--json', 'Output as JSON', false);
}
protected function run(): int
{
$path = realpath($this->getArgument('--path')) ?: $this->getArgument('--path');
$token = $this->getArgument('--token') ?: getenv('GITEA_TOKEN') ?: '';
$org = $this->getArgument('--org');
$repoName = $this->getArgument('--repo');
$apiBase = rtrim($this->getArgument('--api-base'), '/');
$ciMode = (bool) $this->getArgument('--ci');
$jsonMode = (bool) $this->getArgument('--json');
if (!is_dir($path)) {
$this->log('ERROR', "Path does not exist: {$path}");
return 1;
}
if ($repoName === '') {
$repoName = $this->detectRepoName($path);
}
// ── Step 1: Find the Joomla extension manifest XML ──────────
$joomlaXml = $this->findJoomlaManifest($path);
if ($joomlaXml === null) {
$this->log('ERROR', 'No Joomla extension manifest XML found');
return 1;
}
$this->log('INFO', "Joomla manifest: {$joomlaXml['path']}");
// ── Step 2: Load MokoGitea metadata ─────────────────────────
$metadata = $this->loadMetadata($path, $org, $repoName, $token, $apiBase);
if ($metadata === null) {
$this->log('ERROR', 'Could not load MokoGitea metadata');
return 1;
}
// ── Step 3: Compare ─────────────────────────────────────────
$results = $this->compare($metadata, $joomlaXml, $path);
// ── Step 4: Output ──────────────────────────────────────────
if ($jsonMode) {
echo json_encode([
'repo' => $repoName,
'results' => $results,
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";
} else {
$this->printResults($repoName, $results);
}
$errors = count(array_filter($results, fn($r) => $r['status'] === 'error'));
return ($ciMode && $errors > 0) ? 1 : 0;
}
// =================================================================
// Find Joomla manifest XML
// =================================================================
private function findJoomlaManifest(string $root): ?array
{
// Search common locations for a Joomla extension manifest
$candidates = [];
// Package manifest: source/pkg_*.xml
foreach (glob("{$root}/source/pkg_*.xml") as $file) {
$candidates[] = $file;
}
// Component manifest: source/packages/com_*/[name].xml
foreach (glob("{$root}/source/packages/com_*/*.xml") as $file) {
$basename = basename($file);
// Skip access.xml, config.xml, etc.
if (in_array($basename, ['access.xml', 'config.xml'], true)) {
continue;
}
$candidates[] = $file;
}
// Direct source/*.xml
foreach (glob("{$root}/source/*.xml") as $file) {
if (basename($file) !== 'pkg_mokosuitebackup.xml') {
// Already caught above
}
$candidates[] = $file;
}
// src/ fallback
foreach (glob("{$root}/src/pkg_*.xml") as $file) {
$candidates[] = $file;
}
// Find the first one that has <extension type="...">
foreach (array_unique($candidates) as $file) {
$content = file_get_contents($file);
if ($content === false) {
continue;
}
if (preg_match('/<extension\s[^>]*type=["\']([^"\']+)["\']/', $content, $typeMatch)) {
$xml = @simplexml_load_string($content);
if ($xml === false) {
<<<<<<< HEAD
=======
$relPath = str_replace($root . '/', '', $file);
$relPath = str_replace($root . '\\', '', $relPath);
$this->log('WARN', "Skipping {$relPath}: malformed XML");
>>>>>>> main
continue;
}
$type = strtolower($typeMatch[1]);
$relPath = str_replace($root . '/', '', $file);
$relPath = str_replace($root . '\\', '', $relPath);
return [
'path' => $relPath,
'type' => $type,
'xml' => $xml,
];
}
}
return null;
}
// =================================================================
// Load metadata (from API)
// =================================================================
private function loadMetadata(string $root, string $org, string $repoName, string $token, string $apiBase): ?array
{
<<<<<<< HEAD
if ($token !== '') {
$url = "{$apiBase}/repos/{$org}/{$repoName}/metadata";
$ctx = stream_context_create([
'http' => [
'header' => "Authorization: token {$token}\r\nAccept: application/json\r\n",
'timeout' => 10,
],
]);
$body = @file_get_contents($url, false, $ctx);
if ($body !== false) {
$data = json_decode($body, true);
if (is_array($data)) {
$data['source'] = 'api';
return $data;
}
}
}
return null;
=======
if ($token === '') {
$this->log('ERROR', 'No API token provided (use --token or set GITEA_TOKEN env var)');
return null;
}
$url = "{$apiBase}/repos/{$org}/{$repoName}/metadata";
$ctx = stream_context_create([
'http' => [
'header' => "Authorization: token {$token}\r\nAccept: application/json\r\n",
'timeout' => 10,
'ignore_errors' => true,
],
]);
$body = file_get_contents($url, false, $ctx);
// Extract HTTP status from response headers
$httpCode = 0;
if (isset($http_response_header[0]) && preg_match('/\d{3}/', $http_response_header[0], $m)) {
$httpCode = (int) $m[0];
}
if ($body === false) {
$this->log('ERROR', "Failed to connect to {$url} — check network or TLS configuration");
return null;
}
if ($httpCode === 404) {
$this->log('ERROR', "API endpoint not found: {$url}");
$this->log('ERROR', 'Server may need MokoGitea-Fork >= #650 (metadata endpoint rename)');
return null;
}
if ($httpCode === 401 || $httpCode === 403) {
$this->log('ERROR', "Authentication failed (HTTP {$httpCode}) — check your API token");
return null;
}
if ($httpCode >= 400) {
$this->log('ERROR', "API returned HTTP {$httpCode}: " . substr($body, 0, 200));
return null;
}
$data = json_decode($body, true);
if (!is_array($data)) {
$this->log('ERROR', "API returned invalid JSON from {$url}");
return null;
}
$data['source'] = 'api';
return $data;
>>>>>>> main
}
// =================================================================
// Compare metadata against Joomla manifest
// =================================================================
private function compare(array $metadata, array $joomlaXml, string $root): array
{
$results = [];
$xml = $joomlaXml['xml'];
$type = $joomlaXml['type'];
// 1. Extension type
<<<<<<< HEAD
$metaType = $this->normalizeExtensionType($metadata['extension_type'] ?? '');
=======
$metaType = $this->normalizeExtensionType(
$metadata['extension_type'] ?? $metadata['package_type'] ?? ''
);
>>>>>>> main
$results[] = [
'field' => 'extension_type',
'metadata' => $metaType,
'joomla' => $type,
'status' => ($metaType === $type) ? 'ok' : 'error',
'message' => ($metaType === $type)
? "matches <extension type=\"{$type}\">"
: "metadata has \"{$metaType}\" but Joomla manifest has \"{$type}\"",
];
// 2. Element name
$metaName = strtolower($metadata['name'] ?? '');
$metaElement = $this->deriveElement($metaType, $metaName);
$joomlaElement = $this->extractJoomlaElement($xml, $type);
$elementMatch = ($metaElement === $joomlaElement);
$results[] = [
'field' => 'element',
'metadata' => $metaElement,
'joomla' => $joomlaElement,
'status' => $elementMatch ? 'ok' : 'error',
'message' => $elementMatch
? "derived correctly"
: "metadata derives \"{$metaElement}\" but Joomla uses \"{$joomlaElement}\"",
];
// 3. Version
$metaVersion = $metadata['version'] ?? '';
$joomlaVersion = (string) ($xml->version ?? '');
if ($metaVersion !== '' && $joomlaVersion !== '') {
// Strip dev/rc suffixes for comparison (CI bumps these)
$metaBase = preg_replace('/-(dev|rc|alpha|beta)\d*$/', '', $metaVersion);
$joomlaBase = preg_replace('/-(dev|rc|alpha|beta)\d*$/', '', $joomlaVersion);
$versionMatch = ($metaBase === $joomlaBase);
$results[] = [
'field' => 'version',
'metadata' => $metaVersion,
'joomla' => $joomlaVersion,
'status' => $versionMatch ? 'ok' : 'warn',
'message' => $versionMatch
? 'matches (base version)'
: "metadata has \"{$metaVersion}\" but Joomla has \"{$joomlaVersion}\"",
];
}
// 4. PHP minimum (from composer.json)
$composerPhp = $this->readComposerPhpRequirement($root);
$metaPhp = $metadata['php_minimum'] ?? '';
if ($composerPhp !== '' && $metaPhp !== '') {
$phpMatch = ($metaPhp === $composerPhp);
$results[] = [
'field' => 'php_minimum',
'metadata' => $metaPhp,
'joomla' => $composerPhp . ' (composer.json)',
'status' => $phpMatch ? 'ok' : 'warn',
'message' => $phpMatch
? 'matches composer.json'
: "metadata has \"{$metaPhp}\" but composer.json requires \"{$composerPhp}\"",
];
}
// 5. Description
$metaDesc = $metadata['description'] ?? '';
$joomlaDesc = (string) ($xml->description ?? '');
// Joomla descriptions are often language keys, skip those
if ($metaDesc !== '' && $joomlaDesc !== '' && !str_starts_with($joomlaDesc, 'COM_') && !str_starts_with($joomlaDesc, 'PKG_')) {
$descMatch = ($metaDesc === $joomlaDesc);
$results[] = [
'field' => 'description',
'metadata' => substr($metaDesc, 0, 60) . (strlen($metaDesc) > 60 ? '...' : ''),
'joomla' => substr($joomlaDesc, 0, 60) . (strlen($joomlaDesc) > 60 ? '...' : ''),
'status' => $descMatch ? 'ok' : 'info',
'message' => $descMatch ? 'matches' : 'descriptions differ (informational)',
];
}
return $results;
}
// =================================================================
// Helpers
// =================================================================
/**
* Normalize extension_type — map MokoGitea types to Joomla types.
*/
private function normalizeExtensionType(string $type): string
{
return match (strtolower($type)) {
'joomla-extension' => 'package', // legacy mapping
default => strtolower($type),
};
}
/**
* Derive the Joomla element name from type + name.
* Replicates MokoGitea's cleanJoomlaElement() + prefix logic.
*/
private function deriveElement(string $type, string $name): string
{
// Clean: lowercase, strip non-alphanumeric except . _ -
$clean = strtolower($name);
$clean = preg_replace('/[^a-z0-9._-]/', '', $clean);
$prefix = self::JOOMLA_PREFIX[$type] ?? '';
return $prefix . $clean;
}
/**
* Extract the element name from a Joomla manifest XML.
* Follows the same logic as Joomla's InstallerAdapter::getElement().
*/
private function extractJoomlaElement(\SimpleXMLElement $xml, string $type): string
{
switch ($type) {
case 'package':
$packagename = (string) ($xml->packagename ?? '');
if ($packagename !== '') {
return 'pkg_' . strtolower(preg_replace('/[^a-zA-Z0-9._-]/', '', $packagename));
}
break;
case 'component':
$element = (string) ($xml->element ?? '');
if ($element !== '') {
$element = strtolower($element);
return str_starts_with($element, 'com_') ? $element : 'com_' . $element;
}
$name = (string) ($xml->name ?? '');
$name = strtolower(preg_replace('/[^a-zA-Z0-9._-]/', '', $name));
return str_starts_with($name, 'com_') ? $name : 'com_' . $name;
case 'module':
$element = (string) ($xml->element ?? '');
if ($element !== '') {
return strtolower($element);
}
break;
case 'plugin':
// Plugins derive element from the file attribute
if (isset($xml->files)) {
foreach ($xml->files->children() as $file) {
$plugin = (string) ($file->attributes()->plugin ?? '');
if ($plugin !== '') {
return strtolower($plugin);
}
}
}
break;
case 'library':
$libname = (string) ($xml->libraryname ?? '');
if ($libname !== '') {
return strtolower($libname);
}
break;
}
// Fallback: use <name> tag
$name = (string) ($xml->name ?? '');
return strtolower(preg_replace('/[^a-zA-Z0-9._-]/', '', $name));
}
/**
* Read PHP version requirement from composer.json.
*/
private function readComposerPhpRequirement(string $root): string
{
$composerFile = "{$root}/composer.json";
if (!is_file($composerFile)) {
return '';
}
$data = json_decode(file_get_contents($composerFile), true);
if (!is_array($data)) {
return '';
}
$phpReq = $data['require']['php'] ?? '';
// Extract version number from constraint like ">=8.1"
if (preg_match('/(\d+\.\d+)/', $phpReq, $m)) {
return $m[1];
}
return '';
}
private function detectRepoName(string $root): string
{
$gitConfig = "{$root}/.git/config";
if (!file_exists($gitConfig)) {
return basename($root);
}
$content = file_get_contents($gitConfig);
if (preg_match('/url\s*=\s*.*\/([^\/\s]+?)(?:\.git)?\s*$/m', $content, $m)) {
return $m[1];
}
return basename($root);
}
// =================================================================
// Output
// =================================================================
private function printResults(string $repoName, array $results): void
{
$errors = count(array_filter($results, fn($r) => $r['status'] === 'error'));
$warns = count(array_filter($results, fn($r) => $r['status'] === 'warn'));
$oks = count(array_filter($results, fn($r) => $r['status'] === 'ok'));
$this->log('INFO', "Validating {$repoName} Joomla metadata...\n");
foreach ($results as $r) {
$icon = match ($r['status']) {
'ok' => "\xE2\x9C\x93", // ✓
'error' => "\xE2\x9C\x97", // ✗
'warn' => "\xE2\x9A\xA0", // ⚠
default => "\xE2\x84\xB9", //
};
$line = sprintf(
" %s %-16s %s",
$icon,
$r['field'],
$r['message']
);
$this->log(
match ($r['status']) {
'error' => 'ERROR',
'warn' => 'WARN',
'ok' => 'OK',
default => 'INFO',
},
$line
);
}
echo "\n";
if ($errors > 0) {
$this->log('ERROR', "{$errors} error(s) — update delivery will fail");
} elseif ($warns > 0) {
$this->log('WARN', "All critical checks passed, {$warns} warning(s)");
} else {
$this->log('OK', "All {$oks} checks passed");
}
}
}
$app = new JoomlaMetadataValidateCli();
exit($app->execute());
+19 -8
View File
@@ -8,9 +8,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/joomla_release.php * PATH: /cli/joomla_release.php
* BRIEF: Joomla release pipeline — build ZIP+tar.gz, upload to GitHub Release, update updates.xml * BRIEF: Joomla release pipeline — build ZIP+tar.gz, upload to GitHub Release, update updates.xml
* *
@@ -25,7 +31,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
use MokoEnterprise\{ApiClient, AuditLogger, CliFramework, Config, PlatformAdapterFactory}; use MokoCli\{ApiClient, AuditLogger, CliFramework, Config, PlatformAdapterFactory, SourceResolver};
/** /**
* Joomla Release Manager * Joomla Release Manager
@@ -57,7 +63,7 @@ class JoomlaRelease extends CliFramework
]; ];
private ApiClient $api; private ApiClient $api;
private \MokoEnterprise\GitPlatformAdapter $adapter; private \MokoCli\GitPlatformAdapter $adapter;
protected function configure(): void protected function configure(): void
{ {
@@ -121,11 +127,12 @@ class JoomlaRelease extends CliFramework
$this->log('INFO', "Version: {$displayVersion} | Release tag: {$releaseTag}"); $this->log('INFO', "Version: {$displayVersion} | Release tag: {$releaseTag}");
// ── Step 3: Build packages ──────────────────────────────────── // ── Step 3: Build packages ────────────────────────────────────
$srcDir = is_dir("{$path}/src") ? "{$path}/src" : (is_dir("{$path}/htdocs") ? "{$path}/htdocs" : null); $srcDir = SourceResolver::resolveAbsolute($path);
if ($srcDir === null) { if ($srcDir === null) {
$this->log('ERROR', 'No src/ or htdocs/ directory'); $this->log('ERROR', 'No source/ or src/ directory');
return 1; return 1;
} }
SourceResolver::warnIfLegacy($path);
$prefix = $this->typePrefix($meta); $prefix = $this->typePrefix($meta);
$zipName = "{$prefix}{$meta['element']}-{$displayVersion}.zip"; $zipName = "{$prefix}{$meta['element']}-{$displayVersion}.zip";
@@ -406,7 +413,11 @@ class JoomlaRelease extends CliFramework
$this->api->post("/repos/{$repo}/releases", [ $this->api->post("/repos/{$repo}/releases", [
'tag_name' => $tag, 'tag_name' => $tag,
'name' => $releaseName, 'name' => $releaseName,
'body' => "## {$version}\n\nCreated by moko-platform release pipeline.", <<<<<<< HEAD
'body' => "## {$version}\n\nCreated by MokoCLI release pipeline.",
=======
'body' => "## {$version}\n\nCreated by mokoplatform release pipeline.",
>>>>>>> main
'prerelease' => ($stability !== 'stable'), 'prerelease' => ($stability !== 'stable'),
]); ]);
} }
+10 -4
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/license_manage.php * PATH: /cli/license_manage.php
* BRIEF: Manage license packages and keys via MokoGitea licensing API * BRIEF: Manage license packages and keys via MokoGitea licensing API
* *
@@ -28,7 +34,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class LicenseManage extends CliFramework class LicenseManage extends CliFramework
{ {
+749
View File
@@ -0,0 +1,749 @@
#!/usr/bin/env php
<?php
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* FILE INFORMATION
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/manifest_detect.php
* VERSION: 09.32.00
* BRIEF: Auto-detect manifest fields from source files and optionally push to API
*/
declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{CliFramework, SourceResolver};
class ManifestDetectCli extends CliFramework
{
protected function configure(): void
{
$this->setDescription('Auto-detect manifest fields from source files');
$this->addArgument('--path', 'Repository root path', '.');
$this->addArgument('--json', 'Output as JSON', false);
$this->addArgument('--diff', 'Show diff against current manifest API values', false);
$this->addArgument('--update', 'Push detected fields to manifest API', false);
$this->addArgument('--token', 'Gitea API token (or GITEA_TOKEN env)', '');
$this->addArgument('--api-base', 'Gitea API base URL', 'https://git.mokoconsulting.tech/api/v1');
$this->addArgument('--org', 'Gitea org', 'MokoConsulting');
$this->addArgument('--repo', 'Gitea repo name (auto-detected from remote if empty)', '');
$this->addArgument('--github-output', 'Append fields to $GITHUB_OUTPUT', false);
}
protected function run(): int
{
$path = $this->getArgument('--path');
$jsonMode = (bool) $this->getArgument('--json');
$diffMode = (bool) $this->getArgument('--diff');
$updateMode = (bool) $this->getArgument('--update');
$ghOutput = (bool) $this->getArgument('--github-output');
$token = $this->getArgument('--token') ?: getenv('GITEA_TOKEN') ?: '';
$apiBase = rtrim($this->getArgument('--api-base'), '/');
$org = $this->getArgument('--org');
$repoName = $this->getArgument('--repo');
$root = realpath($path) ?: $path;
if (!is_dir($root)) {
$this->log('ERROR', "Path does not exist: {$path}");
return 1;
}
// Auto-detect repo name from git remote
if ($repoName === '') {
$repoName = $this->detectRepoName($root);
}
// ── Detect all fields ───────────────────────────────────────
$detected = $this->detectAll($root, $repoName);
// ── Warn about missing fields ────────────────────────────────
$expected = ['platform', 'name', 'version', 'package_type', 'language', 'entry_point'];
foreach ($expected as $field) {
if (!isset($detected[$field]) || $detected[$field] === '') {
$this->log('WARN', "Could not detect: {$field}");
}
}
// ── Output ──────────────────────────────────────────────────
if ($diffMode || $updateMode) {
if ($token === '') {
$this->log('ERROR', 'API token required for --diff/--update (use --token or GITEA_TOKEN env)');
return 1;
}
if ($repoName === '') {
$this->log('ERROR', 'Could not determine repo name (use --repo)');
return 1;
}
$current = $this->fetchManifest($apiBase, $org, $repoName, $token);
if ($current === null) {
$this->log('ERROR', 'Failed to fetch current manifest from API');
return 1;
}
$changes = $this->computeDiff($current, $detected);
if ($diffMode) {
if (empty($changes)) {
$this->log('INFO', 'No differences — manifest matches source');
} else {
$this->sectionHeader('Manifest Drift');
foreach ($changes as $field => $info) {
$this->log('WARN', sprintf(
'%-20s API: %-30s Detected: %s',
$field,
$info['current'] === '' ? '(empty)' : $info['current'],
$info['detected']
));
}
}
}
if ($updateMode) {
if (empty($changes)) {
$this->log('INFO', 'Nothing to update');
} else {
$update = array_map(fn($i) => $i['detected'], $changes);
$ok = $this->pushManifest($apiBase, $org, $repoName, $token, $current, $update);
if ($ok) {
$this->log('OK', 'Updated ' . count($update) . ' field(s): ' . implode(', ', array_keys($update)));
} else {
$this->log('ERROR', 'Failed to push manifest update');
return 1;
}
}
}
return 0;
}
if ($ghOutput) {
$outputFile = getenv('GITHUB_OUTPUT');
$lines = [];
foreach ($detected as $k => $v) {
$envKey = str_replace('-', '_', $k);
$lines[] = "{$envKey}={$v}";
}
if ($outputFile !== false && $outputFile !== '') {
file_put_contents($outputFile, implode("\n", $lines) . "\n", FILE_APPEND);
$this->log('INFO', 'Wrote ' . count($detected) . ' fields to GITHUB_OUTPUT');
} else {
$this->log('WARN', 'GITHUB_OUTPUT not set — printing to stdout instead');
echo implode("\n", $lines) . "\n";
}
return 0;
}
if ($jsonMode) {
echo json_encode($detected, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";
} else {
foreach ($detected as $k => $v) {
echo "{$k}={$v}\n";
}
}
return 0;
}
// =====================================================================
// Detection engine
// =====================================================================
private function detectAll(string $root, string $repoName): array
{
$platform = $this->detectPlatform($root);
$fields = [
'platform' => $platform,
'name' => '',
'description' => '',
'version' => '',
'element_name' => '',
'package_type' => '',
'language' => '',
'entry_point' => '',
'license_spdx' => '',
'display_name' => '',
'target_version' => '',
'php_minimum' => '',
];
switch ($platform) {
case 'joomla':
$this->detectJoomla($root, $repoName, $fields);
break;
case 'dolibarr':
$this->detectDolibarr($root, $repoName, $fields);
break;
case 'go':
$this->detectGo($root, $repoName, $fields);
break;
case 'mcp':
$this->detectNode($root, $repoName, $fields);
break;
case 'node':
$this->detectNode($root, $repoName, $fields);
$fields['platform'] = 'node';
break;
default:
$this->detectGeneric($root, $repoName, $fields);
break;
}
// Fallbacks
if ($fields['name'] === '') {
$fields['name'] = $repoName ?: basename($root);
}
if ($fields['entry_point'] === '') {
$fields['entry_point'] = $this->detectEntryPoint($root);
}
if ($fields['license_spdx'] === '') {
$fields['license_spdx'] = $this->detectLicense($root);
}
// description: only from platform-specific source, never guessed
// Strip empty values
return array_filter($fields, fn($v) => $v !== '');
}
// ── Platform detection ──────────────────────────────────────────
private function detectPlatform(string $root): string
{
// Joomla: look for pkg_*.xml or extension XML in source dirs
$joomlaXmls = array_merge(
SourceResolver::globSource($root, 'pkg_*.xml'),
glob("{$root}/pkg_*.xml") ?: []
);
if (!empty($joomlaXmls)) {
return 'joomla';
}
// Check source dirs for any Joomla extension XML
foreach (SourceResolver::globSource($root, '*.xml') as $xmlFile) {
$content = file_get_contents($xmlFile);
if (strpos($content, '<extension') !== false) {
return 'joomla';
}
}
// Dolibarr: mod*.class.php with DolibarrModules
$modFiles = array_merge(
SourceResolver::globSource($root, 'core/modules/mod*.class.php'),
glob("{$root}/core/modules/mod*.class.php") ?: []
);
foreach ($modFiles as $file) {
if (strpos(file_get_contents($file), 'DolibarrModules') !== false) {
return 'dolibarr';
}
}
// Go
if (file_exists("{$root}/go.mod")) {
return 'go';
}
// MCP: package.json with mcp-related content
if (file_exists("{$root}/package.json")) {
$pkg = json_decode(file_get_contents("{$root}/package.json"), true) ?? [];
$deps = array_merge(
array_keys($pkg['dependencies'] ?? []),
array_keys($pkg['devDependencies'] ?? [])
);
foreach ($deps as $dep) {
if (strpos($dep, '@modelcontextprotocol/') === 0 || $dep === '@anthropic/mcp-sdk') {
return 'mcp';
}
}
return 'node';
}
// Python
if (file_exists("{$root}/pyproject.toml") || file_exists("{$root}/setup.py")) {
return 'python';
}
return 'generic';
}
// ── Joomla ──────────────────────────────────────────────────────
private function detectJoomla(string $root, string $repoName, array &$fields): void
{
$fields['language'] = 'PHP';
// Find the primary extension manifest XML
$extManifest = $this->findJoomlaManifest($root);
if ($extManifest === null) {
return;
}
$xml = file_get_contents($extManifest);
// Type
$extType = '';
if (preg_match('/type="([^"]*)"/', $xml, $m)) {
$extType = $m[1];
}
$fields['package_type'] = $extType;
// Element name
$element = '';
if (preg_match('/<element>([^<]+)<\/element>/', $xml, $m)) {
$element = $m[1];
}
if ($element === '' && preg_match('/module="([^"]*)"/', $xml, $m)) {
$element = $m[1];
}
if ($element === '' && preg_match('/plugin="([^"]*)"/', $xml, $m)) {
$element = $m[1];
}
if ($extType === 'package' && preg_match('/<packagename>([^<]+)<\/packagename>/', $xml, $m)) {
$element = $m[1];
}
if ($element === '') {
$element = strtolower(basename($extManifest, '.xml'));
}
// Ensure element has type prefix (API stores full element_name like pkg_mokosuite)
$prefixMap = [
'package' => 'pkg_', 'component' => 'com_', 'module' => 'mod_',
'template' => 'tpl_', 'library' => 'lib_', 'file' => 'file_',
];
if (isset($prefixMap[$extType])) {
$prefix = $prefixMap[$extType];
// Only add prefix if not already present (check all known prefixes)
$hasPrefix = false;
foreach ($prefixMap as $p) {
if (strpos($element, $p) === 0) { $hasPrefix = true; break; }
}
if (strpos($element, 'plg_') === 0) { $hasPrefix = true; }
if (!$hasPrefix) {
$element = $prefix . $element;
}
} elseif ($extType === 'plugin') {
$folder = '';
if (preg_match('/group="([^"]*)"/', $xml, $gm)) {
$folder = $gm[1];
}
if ($folder !== '' && strpos($element, 'plg_') !== 0) {
$element = "plg_{$folder}_" . $element;
}
}
$fields['element_name'] = $element;
// Name
if (preg_match('/<name>([^<]+)<\/name>/', $xml, $m)) {
$fields['name'] = trim($m[1]);
}
// Version
if (preg_match('/<version>([^<]+)<\/version>/', $xml, $m)) {
$fields['version'] = trim($m[1]);
}
// Description
if (preg_match('/<description>([^<]+)<\/description>/', $xml, $m)) {
$desc = trim($m[1]);
// Skip language string keys like COM_MOKOSUITE_DESCRIPTION
if (strpos($desc, '_') === false || strlen($desc) > 60) {
$fields['description'] = $desc;
}
}
// Display name for update feeds
if (!empty($fields['name'])) {
$name = $fields['name'];
// If name already has "Type - " prefix, use as-is
if (preg_match('/^(Package|Component|Module|Plugin|Template|Library)\s*-\s*/i', $name)) {
$fields['display_name'] = $name;
} elseif (!empty($extType)) {
$fields['display_name'] = ucfirst($extType) . ' - ' . $name;
}
}
// Target Joomla version
if (preg_match('/<targetplatform\s[^>]*version="([^"]+)"/', $xml, $m)) {
$fields['target_version'] = trim($m[1]);
} else {
// Default for Joomla 5/6
$fields['target_version'] = '(5|6)\..*';
}
// PHP minimum
if (preg_match('/<php_minimum>([^<]+)<\/php_minimum>/', $xml, $m)) {
$fields['php_minimum'] = trim($m[1]);
}
// License
if (preg_match('/<license>([^<]+)<\/license>/', $xml, $m)) {
$fields['license_spdx'] = $this->normalizeLicense(trim($m[1]));
}
}
private function findJoomlaManifest(string $root): ?string
{
// Priority: pkg_*.xml (package manifest)
$pkgXmls = array_merge(
SourceResolver::globSource($root, 'pkg_*.xml'),
glob("{$root}/pkg_*.xml") ?: []
);
if (!empty($pkgXmls)) {
return $pkgXmls[0];
}
// Any extension XML in source dir
foreach (SourceResolver::globSource($root, '*.xml') as $file) {
$content = file_get_contents($file);
if (strpos($content, '<extension') !== false) {
return $file;
}
}
// Root level
foreach (glob("{$root}/*.xml") ?: [] as $file) {
$content = file_get_contents($file);
if (strpos($content, '<extension') !== false) {
return $file;
}
}
return null;
}
// ── Dolibarr ────────────────────────────────────────────────────
private function detectDolibarr(string $root, string $repoName, array &$fields): void
{
$fields['language'] = 'PHP';
$fields['package_type'] = 'dolibarr-module';
$modFile = $this->findDolibarrModule($root);
if ($modFile === null) {
return;
}
$content = file_get_contents($modFile);
// Element name from class file
$modBasename = basename($modFile, '.class.php');
$fields['element_name'] = strtolower(preg_replace('/^mod/', '', $modBasename));
// Name
if (preg_match('/\$this->name\s*=\s*[\'"]([^\'"]+)[\'"]/', $content, $m)) {
$fields['name'] = $m[1];
}
// Version
if (preg_match('/\$this->version\s*=\s*[\'"]([^\'"]+)[\'"]/', $content, $m)) {
$fields['version'] = $m[1];
}
// Description
if (preg_match('/\$this->description\s*=\s*[\'"]([^\'"]+)[\'"]/', $content, $m)) {
$desc = $m[1];
if (strpos($desc, '$') === false) {
$fields['description'] = $desc;
}
}
// License
if (preg_match('/SPDX-License-Identifier:\s*(\S+)/', $content, $m)) {
$fields['license_spdx'] = $m[1];
}
}
private function findDolibarrModule(string $root): ?string
{
$candidates = array_merge(
SourceResolver::globSource($root, 'core/modules/mod*.class.php'),
glob("{$root}/core/modules/mod*.class.php") ?: []
);
foreach ($candidates as $file) {
if (strpos(file_get_contents($file), 'DolibarrModules') !== false) {
return $file;
}
}
return null;
}
// ── Go ──────────────────────────────────────────────────────────
private function detectGo(string $root, string $repoName, array &$fields): void
{
$fields['language'] = 'Go';
$fields['package_type'] = 'application';
$fields['entry_point'] = './';
$goMod = "{$root}/go.mod";
if (!file_exists($goMod)) {
return;
}
$content = file_get_contents($goMod);
// Module path → name
if (preg_match('/^module\s+(\S+)/m', $content, $m)) {
$modulePath = $m[1];
$parts = explode('/', $modulePath);
$fields['name'] = end($parts);
}
// Go version
if (preg_match('/^go\s+(\S+)/m', $content, $m)) {
// This is Go language version, not the project version
// Project version comes from git tags or source files
}
// License
$fields['license_spdx'] = $this->detectLicense($root);
}
// ── Node / MCP ──────────────────────────────────────────────────
private function detectNode(string $root, string $repoName, array &$fields): void
{
$pkgFile = "{$root}/package.json";
if (!file_exists($pkgFile)) {
return;
}
$pkg = json_decode(file_get_contents($pkgFile), true) ?? [];
$fields['name'] = $pkg['name'] ?? '';
// Strip npm scope
if (strpos($fields['name'], '/') !== false) {
$fields['name'] = explode('/', $fields['name'])[1];
}
$fields['version'] = $pkg['version'] ?? '';
$fields['description'] = $pkg['description'] ?? '';
$fields['license_spdx'] = $pkg['license'] ?? '';
// Language detection
if (file_exists("{$root}/tsconfig.json")) {
$fields['language'] = 'TypeScript';
} else {
$fields['language'] = 'JavaScript';
}
// Package type
$deps = array_merge(
array_keys($pkg['dependencies'] ?? []),
array_keys($pkg['devDependencies'] ?? [])
);
$isMcp = false;
foreach ($deps as $dep) {
if (strpos($dep, '@modelcontextprotocol/') === 0 || $dep === '@anthropic/mcp-sdk') {
$isMcp = true;
break;
}
}
$fields['package_type'] = $isMcp ? 'mcp-server' : 'application';
// Entry point
if (file_exists("{$root}/dist")) {
$fields['entry_point'] = 'dist/';
} elseif (file_exists("{$root}/src")) {
$fields['entry_point'] = 'src/';
} else {
$fields['entry_point'] = './';
}
}
// ── Generic ─────────────────────────────────────────────────────
private function detectGeneric(string $root, string $repoName, array &$fields): void
{
$fields['package_type'] = 'generic';
// Try to detect language from file extensions
$fields['language'] = $this->detectLanguageFromFiles($root);
$fields['license_spdx'] = $this->detectLicense($root);
}
// =====================================================================
// Shared detection helpers
// =====================================================================
private function detectEntryPoint(string $root): string
{
$abs = SourceResolver::resolveAbsolute($root);
if ($abs !== null) {
return basename($abs) . '/';
}
if (is_dir("{$root}/dist")) return 'dist/';
if (is_dir("{$root}/src")) return 'src/';
return './';
}
private function detectLicense(string $root): string
{
// Check LICENSE file
foreach (['LICENSE', 'LICENSE.md', 'LICENSE.txt', 'COPYING'] as $name) {
$file = "{$root}/{$name}";
if (!file_exists($file)) continue;
$content = file_get_contents($file);
// SPDX header
if (preg_match('/SPDX-License-Identifier:\s*(\S+)/', $content, $m)) {
return $m[1];
}
// Common license patterns
if (strpos($content, 'GNU GENERAL PUBLIC LICENSE') !== false) {
if (strpos($content, 'Version 3') !== false) return 'GPL-3.0-or-later';
if (strpos($content, 'Version 2') !== false) return 'GPL-2.0-or-later';
}
if (strpos($content, 'MIT License') !== false) return 'MIT';
if (strpos($content, 'Apache License') !== false && strpos($content, 'Version 2.0') !== false) return 'Apache-2.0';
}
return '';
}
private function detectLanguageFromFiles(string $root): string
{
$counts = ['PHP' => 0, 'Go' => 0, 'TypeScript' => 0, 'JavaScript' => 0, 'Python' => 0, 'Shell' => 0];
$extensions = [
'php' => 'PHP', 'go' => 'Go', 'ts' => 'TypeScript',
'js' => 'JavaScript', 'py' => 'Python', 'sh' => 'Shell',
];
// Quick scan: only check top two levels
foreach (glob("{$root}/*") ?: [] as $item) {
$ext = pathinfo($item, PATHINFO_EXTENSION);
if (isset($extensions[$ext])) {
$counts[$extensions[$ext]]++;
}
if (is_dir($item) && basename($item)[0] !== '.') {
foreach (glob("{$item}/*") ?: [] as $subItem) {
$ext = pathinfo($subItem, PATHINFO_EXTENSION);
if (isset($extensions[$ext])) {
$counts[$extensions[$ext]]++;
}
}
}
}
arsort($counts);
$top = key($counts);
return $counts[$top] > 0 ? $top : '';
}
private function normalizeLicense(string $license): string
{
$lower = strtolower($license);
$isGpl = strpos($lower, 'gpl') !== false || strpos($lower, 'general public license') !== false;
if ($isGpl && strpos($lower, '3') !== false) return 'GPL-3.0-or-later';
if ($isGpl && strpos($lower, '2') !== false) return 'GPL-2.0-or-later';
if ($lower === 'mit' || strpos($lower, 'mit license') !== false) return 'MIT';
if (strpos($lower, 'apache') !== false) return 'Apache-2.0';
return $license;
}
private function detectRepoName(string $root): string
{
$gitConfig = "{$root}/.git/config";
if (!file_exists($gitConfig)) {
return basename($root);
}
$content = file_get_contents($gitConfig);
if (preg_match('/url\s*=\s*.*\/([^\/\s]+?)(?:\.git)?\s*$/m', $content, $m)) {
return $m[1];
}
return basename($root);
}
// =====================================================================
// API interaction
// =====================================================================
private function fetchManifest(string $apiBase, string $org, string $repo, string $token): ?array
{
$url = "{$apiBase}/repos/{$org}/{$repo}/manifest";
$ctx = stream_context_create([
'http' => [
'header' => "Authorization: token {$token}\r\nAccept: application/json\r\n",
'timeout' => 10,
],
]);
$body = @file_get_contents($url, false, $ctx);
if ($body === false) return null;
return json_decode($body, true);
}
private function computeDiff(array $current, array $detected): array
{
// Map detected keys to API keys (underscores match)
$changes = [];
foreach ($detected as $key => $value) {
$apiKey = $key;
$currentVal = $current[$apiKey] ?? '';
// Only flag as changed if detected value is non-empty and differs
if ($value !== '' && $value !== $currentVal) {
// Don't overwrite a non-empty API value with a detected value
// unless the API value is actually empty
if ($currentVal === '' || $this->shouldOverride($key, $currentVal, $value)) {
$changes[$key] = [
'current' => $currentVal,
'detected' => $value,
];
}
}
}
return $changes;
}
private function shouldOverride(string $field, string $current, string $detected): bool
{
// Version: detected from source is authoritative
if ($field === 'version') return true;
// These fields: source files are authoritative
if (in_array($field, ['element_name', 'package_type', 'language', 'entry_point'], true)) {
return true;
}
// For other fields, only fill empty — don't overwrite manual edits
return false;
}
private function pushManifest(string $apiBase, string $org, string $repo, string $token, array $current, array $update): bool
{
$merged = array_merge($current, $update);
$url = "{$apiBase}/repos/{$org}/{$repo}/manifest";
$payload = json_encode($merged);
$ctx = stream_context_create([
'http' => [
'method' => 'PUT',
'header' => "Authorization: token {$token}\r\nContent-Type: application/json\r\nAccept: application/json\r\n",
'content' => $payload,
'timeout' => 10,
],
]);
$body = @file_get_contents($url, false, $ctx);
return $body !== false;
}
}
$app = new ManifestDetectCli();
exit($app->execute());
+12 -7
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/manifest_element.php * PATH: /cli/manifest_element.php
* BRIEF: Extract element name, type, type prefix, and ZIP name from manifest * BRIEF: Extract element name, type, type prefix, and ZIP name from manifest
*/ */
@@ -17,7 +23,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\{CliFramework, SourceResolver};
class ManifestElementCli extends CliFramework class ManifestElementCli extends CliFramework
{ {
@@ -48,7 +54,7 @@ class ManifestElementCli extends CliFramework
} }
} }
$extManifest = null; $extManifest = null;
$manifestFiles = array_merge(glob("{$root}/src/pkg_*.xml") ?: [], glob("{$root}/src/*.xml") ?: [], glob("{$root}/*.xml") ?: []); $manifestFiles = array_merge(SourceResolver::globSource($root, 'pkg_*.xml'), SourceResolver::globSource($root, '*.xml'), glob("{$root}/*.xml") ?: []);
foreach ($manifestFiles as $file) { foreach ($manifestFiles as $file) {
$c = file_get_contents($file); $c = file_get_contents($file);
if (strpos($c, '<extension') !== false) { if (strpos($c, '<extension') !== false) {
@@ -58,8 +64,7 @@ class ManifestElementCli extends CliFramework
} }
$modFile = null; $modFile = null;
$modFiles = array_merge( $modFiles = array_merge(
glob("{$root}/src/core/modules/mod*.class.php") ?: [], SourceResolver::globSource($root, 'core/modules/mod*.class.php'),
glob("{$root}/htdocs/core/modules/mod*.class.php") ?: [],
glob("{$root}/core/modules/mod*.class.php") ?: [] glob("{$root}/core/modules/mod*.class.php") ?: []
); );
foreach ($modFiles as $file) { foreach ($modFiles as $file) {
+564
View File
@@ -0,0 +1,564 @@
#!/usr/bin/env php
<?php
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* FILE INFORMATION
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/manifest_integrity.php
* VERSION: 09.32.00
* BRIEF: Cross-check manifest API fields against repo contents across the org
*/
declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework;
class ManifestIntegrityCli extends CliFramework
{
protected function configure(): void
{
$this->setDescription('Cross-check manifest fields against repo contents across the org');
$this->addArgument('--path', 'Single repo path (local mode)', '');
$this->addArgument('--org', 'Gitea org (bulk mode)', 'MokoConsulting');
$this->addArgument('--repo', 'Single repo name (remote mode)', '');
$this->addArgument('--token', 'Gitea API token (or GITEA_TOKEN env)', '');
$this->addArgument('--api-base', 'Gitea API base URL', 'https://git.mokoconsulting.tech/api/v1');
$this->addArgument('--fix', 'Push fixes for detected drift', false);
$this->addArgument('--json', 'Output as JSON', false);
$this->addArgument('--quiet', 'Only show repos with issues', false);
}
protected function run(): int
{
$path = $this->getArgument('--path');
$org = $this->getArgument('--org');
$repoName = $this->getArgument('--repo');
$token = $this->getArgument('--token') ?: getenv('GITEA_TOKEN') ?: '';
$apiBase = rtrim($this->getArgument('--api-base'), '/');
$fixMode = (bool) $this->getArgument('--fix');
$jsonMode = (bool) $this->getArgument('--json');
$quiet = (bool) $this->getArgument('--quiet');
if ($token === '') {
$this->log('ERROR', 'API token required (use --token or GITEA_TOKEN env)');
return 1;
}
// ── Mode selection ──────────────────────────────────────────
if ($path !== '') {
// Local mode: detect from source + compare to API
return $this->checkLocal($path, $org, $repoName, $token, $apiBase, $fixMode, $jsonMode);
}
if ($repoName !== '') {
// Single remote repo
return $this->checkRemoteRepo($org, $repoName, $token, $apiBase, $fixMode, $jsonMode);
}
// Bulk mode: all repos in org
return $this->checkOrg($org, $token, $apiBase, $fixMode, $jsonMode, $quiet);
}
// =====================================================================
// Local mode — detect from source, compare to API
// =====================================================================
private function checkLocal(string $path, string $org, string $repoName, string $token, string $apiBase, bool $fix, bool $json): int
{
$root = realpath($path) ?: $path;
if (!is_dir($root)) {
$this->log('ERROR', "Path does not exist: {$path}");
return 1;
}
if ($repoName === '') {
$repoName = $this->detectRepoName($root);
}
// Run manifest_detect logic
$detected = $this->runDetect($root, $repoName);
$current = $this->fetchManifest($apiBase, $org, $repoName, $token);
if ($current === null) {
$this->log('ERROR', "Failed to fetch manifest for {$org}/{$repoName}");
return 1;
}
$issues = $this->validate($current, $detected, $repoName);
if ($json) {
echo json_encode(['repo' => $repoName, 'issues' => $issues], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";
} else {
$this->printIssues($repoName, $issues);
}
if ($fix && !empty($issues)) {
return $this->applyFixes($apiBase, $org, $repoName, $token, $current, $issues);
}
return empty($issues) ? 0 : 1;
}
// =====================================================================
// Remote single repo mode — fetch source files via API
// =====================================================================
private function checkRemoteRepo(string $org, string $repoName, string $token, string $apiBase, bool $fix, bool $json): int
{
$current = $this->fetchManifest($apiBase, $org, $repoName, $token);
if ($current === null) {
$this->log('ERROR', "Failed to fetch manifest for {$org}/{$repoName}");
return 1;
}
$issues = $this->validateManifestOnly($current, $repoName);
if ($json) {
echo json_encode(['repo' => $repoName, 'issues' => $issues], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";
} else {
$this->printIssues($repoName, $issues);
}
if ($fix && !empty($issues)) {
return $this->applyFixes($apiBase, $org, $repoName, $token, $current, $issues);
}
return empty($issues) ? 0 : 1;
}
// =====================================================================
// Bulk org mode — check all repos
// =====================================================================
private function checkOrg(string $org, string $token, string $apiBase, bool $fix, bool $json, bool $quiet): int
{
$repos = $this->fetchOrgRepos($apiBase, $org, $token);
if ($repos === null) {
$this->log('ERROR', "Failed to fetch repos for org {$org}");
return 1;
}
$this->log('INFO', "Manifest Integrity Check — {$org} (" . count($repos) . " repos)");
$allResults = [];
$totalIssues = 0;
$reposWithIssues = 0;
foreach ($repos as $repo) {
$name = $repo['name'];
$manifest = $this->fetchManifest($apiBase, $org, $name, $token);
if ($manifest === null) {
if (!$quiet) {
$this->log('WARN', "{$name}: no manifest");
}
continue;
}
$issues = $this->validateManifestOnly($manifest, $name);
if (!empty($issues)) {
$reposWithIssues++;
$totalIssues += count($issues);
if ($json) {
$allResults[] = ['repo' => $name, 'issues' => $issues];
} else {
$this->printIssues($name, $issues);
}
if ($fix) {
$this->applyFixes($apiBase, $org, $name, $token, $manifest, $issues);
}
} elseif (!$quiet && !$json) {
$this->log('OK', "{$name}: clean");
}
}
if ($json) {
echo json_encode($allResults, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";
} else {
echo "\n";
$level = $reposWithIssues > 0 ? 'WARN' : 'OK';
$this->log($level, sprintf(
'Summary: %d repos checked, %d with issues (%d total issues)',
count($repos),
$reposWithIssues,
$totalIssues
));
}
return $reposWithIssues > 0 ? 1 : 0;
}
// =====================================================================
// Validation rules
// =====================================================================
/**
* Full validation: compare API manifest against locally-detected fields.
*/
private function validate(array $current, array $detected, string $repoName): array
{
$issues = [];
// Required fields that should never be empty
$required = ['platform', 'name', 'version', 'package_type', 'language', 'entry_point'];
foreach ($required as $field) {
if (empty($current[$field])) {
$fix = $detected[$field] ?? null;
$issues[] = [
'field' => $field,
'severity' => 'error',
'message' => 'Missing required field',
'current' => '',
'fix' => $fix,
];
}
}
// Drift detection: detected value differs from API
foreach ($detected as $field => $detectedValue) {
$currentValue = $current[$field] ?? '';
if ($detectedValue !== '' && $currentValue !== '' && $detectedValue !== $currentValue) {
// Version drift is expected on dev branches (suffix)
if ($field === 'version' && strpos($detectedValue, $currentValue) === 0) {
continue; // e.g., detected "02.34.50-dev" vs API "02.34.50"
}
if ($field === 'version' && strpos($currentValue, $detectedValue) === 0) {
continue;
}
$issues[] = [
'field' => $field,
'severity' => 'warn',
'message' => 'Drift: source differs from manifest',
'current' => $currentValue,
'fix' => $detectedValue,
];
}
}
// Platform-specific structure validation
$platform = $current['platform'] ?? '';
$issues = array_merge($issues, $this->validatePlatformStructure($platform, $current, $repoName));
return $issues;
}
/**
* API-only validation: check manifest fields for completeness and consistency
* without access to source files.
*/
private function validateManifestOnly(array $manifest, string $repoName): array
{
$issues = [];
// Required fields
$required = ['platform', 'name', 'version', 'language'];
foreach ($required as $field) {
if (empty($manifest[$field])) {
$issues[] = [
'field' => $field,
'severity' => 'error',
'message' => 'Missing required field',
'current' => '',
'fix' => null,
];
}
}
// Recommended fields
$recommended = ['package_type', 'entry_point', 'license_spdx', 'description'];
foreach ($recommended as $field) {
if (empty($manifest[$field])) {
$issues[] = [
'field' => $field,
'severity' => 'info',
'message' => 'Recommended field is empty',
'current' => '',
'fix' => null,
];
}
}
// Platform-specific checks
$platform = $manifest['platform'] ?? '';
$issues = array_merge($issues, $this->validatePlatformStructure($platform, $manifest, $repoName));
return $issues;
}
/**
* Platform-specific validation rules.
*/
private function validatePlatformStructure(string $platform, array $manifest, string $repoName): array
{
$issues = [];
switch ($platform) {
case 'joomla':
case 'waas-component':
// Joomla repos must have element_name
if (empty($manifest['element_name'])) {
$issues[] = [
'field' => 'element_name',
'severity' => 'error',
'message' => 'Joomla repos require element_name',
'current' => '',
'fix' => null,
];
}
// Language should be PHP
if (!empty($manifest['language']) && $manifest['language'] !== 'PHP') {
$issues[] = [
'field' => 'language',
'severity' => 'warn',
'message' => 'Joomla repos should have language=PHP',
'current' => $manifest['language'],
'fix' => 'PHP',
];
}
break;
case 'dolibarr':
case 'crm-module':
if (!empty($manifest['language']) && $manifest['language'] !== 'PHP') {
$issues[] = [
'field' => 'language',
'severity' => 'warn',
'message' => 'Dolibarr repos should have language=PHP',
'current' => $manifest['language'],
'fix' => 'PHP',
];
}
break;
case 'go':
if (!empty($manifest['language']) && $manifest['language'] !== 'Go') {
$issues[] = [
'field' => 'language',
'severity' => 'warn',
'message' => 'Go repos should have language=Go',
'current' => $manifest['language'],
'fix' => 'Go',
];
}
break;
case 'mcp':
if (!empty($manifest['language']) && !in_array($manifest['language'], ['TypeScript', 'JavaScript'], true)) {
$issues[] = [
'field' => 'language',
'severity' => 'warn',
'message' => 'MCP repos should have language=TypeScript or JavaScript',
'current' => $manifest['language'],
'fix' => null,
];
}
break;
}
// Version format check: should be XX.YY.ZZ
$version = $manifest['version'] ?? '';
if ($version !== '' && !preg_match('/^\d{2}\.\d{2}\.\d{2}/', $version)) {
// Allow semver for node/go repos
if (!in_array($platform, ['mcp', 'node', 'go'], true)) {
$issues[] = [
'field' => 'version',
'severity' => 'info',
'message' => 'Version does not match XX.YY.ZZ format',
'current' => $version,
'fix' => null,
];
}
}
return $issues;
}
// =====================================================================
// Output
// =====================================================================
private function printIssues(string $repoName, array $issues): void
{
if (empty($issues)) {
return;
}
$errors = count(array_filter($issues, fn($i) => $i['severity'] === 'error'));
$warns = count(array_filter($issues, fn($i) => $i['severity'] === 'warn'));
$infos = count($issues) - $errors - $warns;
echo "\n";
$summary = [];
if ($errors > 0) $summary[] = "{$errors} error(s)";
if ($warns > 0) $summary[] = "{$warns} warning(s)";
if ($infos > 0) $summary[] = "{$infos} info";
$this->log($errors > 0 ? 'ERROR' : 'WARN', "{$repoName}" . implode(', ', $summary));
foreach ($issues as $issue) {
$icon = match ($issue['severity']) {
'error' => 'ERROR',
'warn' => 'WARN',
default => 'INFO',
};
$msg = sprintf(' %-18s %s', $issue['field'], $issue['message']);
if ($issue['current'] !== '') {
$msg .= " (current: {$issue['current']})";
}
if ($issue['fix'] !== null) {
$msg .= " → fix: {$issue['fix']}";
}
$this->log($icon, $msg);
}
}
// =====================================================================
// Fix application
// =====================================================================
private function applyFixes(string $apiBase, string $org, string $repo, string $token, array $current, array $issues): int
{
$fixes = [];
foreach ($issues as $issue) {
if ($issue['fix'] !== null && $issue['fix'] !== '') {
$fixes[$issue['field']] = $issue['fix'];
}
}
if (empty($fixes)) {
$this->log('INFO', "{$repo}: no auto-fixable issues");
return 0;
}
$merged = array_merge($current, $fixes);
$url = "{$apiBase}/repos/{$org}/{$repo}/manifest";
$payload = json_encode($merged);
$ctx = stream_context_create([
'http' => [
'method' => 'PUT',
'header' => "Authorization: token {$token}\r\nContent-Type: application/json\r\nAccept: application/json\r\n",
'content' => $payload,
'timeout' => 10,
],
]);
$body = @file_get_contents($url, false, $ctx);
if ($body === false) {
$this->log('ERROR', "{$repo}: failed to push fixes");
return 1;
}
$this->log('OK', "{$repo}: fixed " . implode(', ', array_keys($fixes)));
return 0;
}
// =====================================================================
// API helpers
// =====================================================================
private function fetchManifest(string $apiBase, string $org, string $repo, string $token): ?array
{
$url = "{$apiBase}/repos/{$org}/{$repo}/manifest";
$ctx = stream_context_create([
'http' => [
'header' => "Authorization: token {$token}\r\nAccept: application/json\r\n",
'timeout' => 10,
],
]);
$body = @file_get_contents($url, false, $ctx);
if ($body === false) return null;
$data = json_decode($body, true);
return is_array($data) ? $data : null;
}
private function fetchOrgRepos(string $apiBase, string $org, string $token): ?array
{
$allRepos = [];
$page = 1;
$limit = 50;
while (true) {
$url = "{$apiBase}/orgs/{$org}/repos?page={$page}&limit={$limit}";
$ctx = stream_context_create([
'http' => [
'header' => "Authorization: token {$token}\r\nAccept: application/json\r\n",
'timeout' => 15,
],
]);
$body = @file_get_contents($url, false, $ctx);
if ($body === false) return null;
$repos = json_decode($body, true);
if (!is_array($repos) || empty($repos)) break;
$allRepos = array_merge($allRepos, $repos);
if (count($repos) < $limit) break;
$page++;
}
// Filter out archived and empty repos
return array_filter($allRepos, fn($r) => !($r['archived'] ?? false) && !($r['empty'] ?? false));
}
// =====================================================================
// Detection (delegates to manifest_detect logic)
// =====================================================================
private function runDetect(string $root, string $repoName): array
{
$script = __DIR__ . '/manifest_detect.php';
$redirect = PHP_OS_FAMILY === 'Windows' ? '2>NUL' : '2>/dev/null';
$cmd = sprintf(
'php %s --path %s --repo %s --json --quiet %s',
escapeshellarg($script),
escapeshellarg($root),
escapeshellarg($repoName),
$redirect
);
$output = shell_exec($cmd) ?? '';
// Extract JSON object from output (skip banner/log lines)
if (preg_match('/\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/s', $output, $m)) {
$data = json_decode($m[0], true);
if (is_array($data)) {
return $data;
}
}
return [];
}
private function detectRepoName(string $root): string
{
$gitConfig = "{$root}/.git/config";
if (!file_exists($gitConfig)) {
return basename($root);
}
$content = file_get_contents($gitConfig);
if (preg_match('/url\s*=\s*.*\/([^\/\s]+?)(?:\.git)?\s*$/m', $content, $m)) {
return $m[1];
}
return basename($root);
}
}
$app = new ManifestIntegrityCli();
exit($app->execute());
+288
View File
@@ -0,0 +1,288 @@
#!/usr/bin/env php
<?php
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* FILE INFORMATION
<<<<<<< HEAD
* DEFGROUP: MokoCLI.CLI
* INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/manifest_licensing.php
* VERSION: 09.25.05
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/manifest_licensing.php
* VERSION: 09.32.00
>>>>>>> main
* BRIEF: Ensure licensing tags (updateservers, dlid) in Joomla extension manifests
*/
declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{CliFramework, SourceResolver};
/**
* Reads the <licensing> block from .mokogitea/manifest.xml and ensures that the
* Joomla extension manifest contains the correct <updateservers> and <dlid> tags.
*
* manifest.xml licensing block example:
*
* <licensing>
* <enabled>true</enabled>
* <dlid>true</dlid>
* <update-server>https://git.mokoconsulting.tech/{org}/{repo}/updates.xml</update-server>
* <update-server-name>MyExtension Updates</update-server-name>
* </licensing>
*
* Supports {org} and {repo} placeholders in update-server URL, resolved from
* the manifest's <identity> block or git remote.
*/
class ManifestLicensingCli extends CliFramework
{
protected function configure(): void
{
$this->setDescription('Ensure licensing tags (updateservers, dlid) in Joomla extension manifests');
$this->addArgument('--path', 'Repository root path', '.');
$this->addArgument('--fix', 'Apply fixes (default: dry-run check only)', false);
$this->addArgument('--github-output', 'Write results to $GITHUB_OUTPUT', false);
}
protected function run(): int
{
$root = realpath($this->getArgument('--path')) ?: $this->getArgument('--path');
$fix = (bool) $this->getArgument('--fix');
$ghOutput = (bool) $this->getArgument('--github-output');
// ── 1. Read manifest.xml ──────────────────────────────────────────
$manifestFile = "{$root}/.mokogitea/manifest.xml";
if (!file_exists($manifestFile)) {
$this->log('WARN', "No manifest.xml found at {$manifestFile}");
$this->outputResult($ghOutput, 'skipped', 'No manifest.xml');
return 0;
}
$xml = @simplexml_load_file($manifestFile);
if ($xml === false) {
$this->log('ERROR', "Failed to parse {$manifestFile}");
return 1;
}
// ── 2. Check if licensing is enabled ──────────────────────────────
if (!isset($xml->licensing) || (string) ($xml->licensing->enabled ?? '') !== 'true') {
$this->log('INFO', 'Licensing not enabled in manifest.xml — skipping');
$this->outputResult($ghOutput, 'skipped', 'Licensing not enabled');
return 0;
}
$licensingNode = $xml->licensing;
$dlidEnabled = ((string) ($licensingNode->dlid ?? 'true')) === 'true';
$updateServerUrl = (string) ($licensingNode->{'update-server'} ?? '');
$updateServerName = (string) ($licensingNode->{'update-server-name'} ?? '');
// ── 3. Resolve placeholders ───────────────────────────────────────
$org = (string) ($xml->identity->org ?? '');
$repo = (string) ($xml->identity->name ?? '');
// Fallback to git remote if manifest doesn't have org/name
if (empty($org) || empty($repo)) {
$remote = trim((string) @shell_exec("cd " . escapeshellarg($root) . " && git remote get-url origin 2>/dev/null"));
if (preg_match('#[/:]([^/]+)/([^/.]+?)(?:\.git)?$#', $remote, $m)) {
if (empty($org)) {
$org = $m[1];
}
if (empty($repo)) {
$repo = $m[2];
}
}
}
// Default update server URL if not specified
if (empty($updateServerUrl) && !empty($org) && !empty($repo)) {
$updateServerUrl = "https://git.mokoconsulting.tech/{$org}/{$repo}/updates.xml";
}
// Resolve {org} and {repo} placeholders
$updateServerUrl = str_replace(['{org}', '{repo}'], [$org, $repo], $updateServerUrl);
// Default server name from display-name or repo name
if (empty($updateServerName)) {
$displayName = (string) ($xml->identity->{'display-name'} ?? $repo);
$updateServerName = $displayName . ' Updates';
}
if (empty($updateServerUrl)) {
$this->log('ERROR', 'Cannot determine update server URL — set <update-server> in manifest.xml or ensure org/repo are available');
return 1;
}
$this->log('INFO', "Licensing enabled — org={$org}, repo={$repo}");
$this->log('INFO', "Update server: {$updateServerUrl}");
$this->log('INFO', "DLID required: " . ($dlidEnabled ? 'yes' : 'no'));
// ── 4. Find Joomla extension manifests ────────────────────────────
$xmlFiles = array_merge(
SourceResolver::globSource($root, '*.xml'),
SourceResolver::globSource($root, 'packages/*/*.xml'),
glob("{$root}/*.xml") ?: []
);
$packageManifest = null;
foreach ($xmlFiles as $file) {
$content = file_get_contents($file);
if (!str_contains($content, '<extension')) {
continue;
}
// Find the package manifest (type="package") or the main extension manifest
if (str_contains($content, 'type="package"')) {
$packageManifest = $file;
break;
}
// Fallback: first extension manifest found
if ($packageManifest === null) {
$packageManifest = $file;
}
}
if ($packageManifest === null) {
$this->log('WARN', 'No Joomla extension manifest found');
$this->outputResult($ghOutput, 'skipped', 'No extension manifest');
return 0;
}
$relPath = str_replace($root . '/', '', str_replace('\\', '/', $packageManifest));
$this->log('INFO', "Package manifest: {$relPath}");
// ── 5. Check and fix the manifest ─────────────────────────────────
$content = file_get_contents($packageManifest);
$original = $content;
$changes = [];
// --- 5a. Ensure <updateservers> block with correct URL ---
if (preg_match('#<updateservers>\s*</updateservers>#s', $content)) {
// Empty updateservers block — inject the server
$replacement = "<updateservers>\n"
. " <server type=\"extension\" name=\"{$updateServerName}\">{$updateServerUrl}</server>\n"
. " </updateservers>";
$content = preg_replace('#<updateservers>\s*</updateservers>#s', $replacement, $content);
$changes[] = 'Added update server URL to empty <updateservers>';
} elseif (!str_contains($content, '<updateservers>')) {
// No updateservers at all — add before </extension>
$serverBlock = "\n <updateservers>\n"
. " <server type=\"extension\" name=\"{$updateServerName}\">{$updateServerUrl}</server>\n"
. " </updateservers>\n";
$content = str_replace('</extension>', $serverBlock . '</extension>', $content);
$changes[] = 'Added <updateservers> block';
} else {
// updateservers exists — verify URL is correct
if (preg_match('#<server[^>]*>([^<]+)</server>#', $content, $m)) {
if ($m[1] !== $updateServerUrl) {
$content = preg_replace(
'#(<server[^>]*>)[^<]+(</server>)#',
"\${1}{$updateServerUrl}\${2}",
$content
);
$changes[] = "Updated server URL: {$m[1]}{$updateServerUrl}";
}
}
}
// --- 5b. Ensure <dlid> tag if required ---
if ($dlidEnabled) {
if (!str_contains($content, '<dlid')) {
// Add before <updateservers> if present, otherwise before </extension>
$dlidTag = ' <dlid prefix="dlid=" suffix=""/>' . "\n";
if (str_contains($content, '<updateservers>')) {
$content = str_replace('<updateservers>', $dlidTag . "\n <updateservers>", $content);
} else {
$content = str_replace('</extension>', $dlidTag . '</extension>', $content);
}
$changes[] = 'Added <dlid> tag';
}
}
// --- 5c. Ensure <blockChildUninstall> for packages ---
if (str_contains($content, 'type="package"') && !str_contains($content, '<blockChildUninstall>')) {
$blockTag = ' <blockChildUninstall>true</blockChildUninstall>' . "\n";
if (str_contains($content, '<dlid')) {
// Add after <dlid>
$content = preg_replace(
'#(<dlid[^/]*/>\s*\n)#',
"\${1}{$blockTag}",
$content
);
} elseif (str_contains($content, '<updateservers>')) {
$content = str_replace('<updateservers>', $blockTag . "\n <updateservers>", $content);
} else {
$content = str_replace('</extension>', $blockTag . '</extension>', $content);
}
$changes[] = 'Added <blockChildUninstall>true</blockChildUninstall>';
}
// ── 6. Report and apply ───────────────────────────────────────────
if (empty($changes)) {
$this->log('INFO', 'All licensing tags are correct — no changes needed');
$this->outputResult($ghOutput, 'ok', 'No changes needed');
return 0;
}
foreach ($changes as $change) {
$this->log($fix ? 'INFO' : 'WARN', ($fix ? 'Fixed: ' : 'Needs fix: ') . $change);
}
if ($fix) {
file_put_contents($packageManifest, $content);
$this->log('INFO', "Wrote {$relPath} with " . count($changes) . " change(s)");
$this->outputResult($ghOutput, 'fixed', implode('; ', $changes));
} else {
$this->log('WARN', 'Run with --fix to apply changes');
$this->outputResult($ghOutput, 'needs-fix', implode('; ', $changes));
return 1;
}
return 0;
}
/**
* Write result to $GITHUB_OUTPUT if requested.
*/
private function outputResult(bool $ghOutput, string $status, string $detail): void
{
if (!$ghOutput) {
return;
}
$outputFile = getenv('GITHUB_OUTPUT');
if ($outputFile === false || $outputFile === '') {
echo "licensing_status={$status}\n";
echo "licensing_detail={$detail}\n";
return;
}
$fh = fopen($outputFile, 'a');
fwrite($fh, "licensing_status={$status}\n");
fwrite($fh, "licensing_detail={$detail}\n");
fclose($fh);
}
}
$app = new ManifestLicensingCli();
exit($app->execute());
+410 -89
View File
@@ -6,126 +6,448 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/manifest_read.php * PATH: /cli/manifest_read.php
* VERSION: 09.23.00 * VERSION: 09.25.05
* BRIEF: Parse .manifest.xml and output requested field(s) for CI consumption * BRIEF: Parse .manifest.xml and output requested field(s) for CI consumption
=======
* DEFGROUP: mokocli.CLI
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/manifest_read.php
* VERSION: 09.32.00
* BRIEF: Read repo metadata from Gitea manifest API, auto-detect the rest
>>>>>>> main
*/ */
declare(strict_types=1); declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class ManifestReadCli extends CliFramework class ManifestReadCli extends CliFramework
{ {
/** Joomla extension XML element names searched in root and source/ dirs. */
private const JOOMLA_XML_ROOTS = ['extension', 'install'];
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Parse manifest.xml and output requested field(s) for CI consumption'); $this->setDescription('Read repo metadata from Gitea API with auto-detection fallback');
$this->addArgument('--path', 'Repository root path', '.'); $this->addArgument('--path', 'Repository root path', '.');
$this->addArgument('--field', 'Single field name to output', ''); $this->addArgument('--field', 'Single field name to output', '');
$this->addArgument('--all', 'Print all fields as KEY=VALUE lines', false); $this->addArgument('--all', 'Print all fields as KEY=VALUE lines', false);
$this->addArgument('--github-output', 'Append all fields to $GITHUB_OUTPUT', false); $this->addArgument('--github-output', 'Append all fields to $GITHUB_OUTPUT', false);
$this->addArgument('--json', 'Output all fields as JSON', false); $this->addArgument('--json', 'Output all fields as JSON', false);
} }
protected function run(): int protected function run(): int
{ {
$path = $this->getArgument('--path'); $path = $this->getArgument('--path');
$field = $this->getArgument('--field'); $field = $this->getArgument('--field');
$showAll = $this->getArgument('--all'); $showAll = $this->getArgument('--all');
$ghOutput = $this->getArgument('--github-output'); $ghOut = $this->getArgument('--github-output');
$jsonMode = $this->getArgument('--json'); $jsonMode = $this->getArgument('--json');
// Determine mode $mode = match (true) {
if ($ghOutput) { (bool) $ghOut => 'github-output',
$mode = 'github-output'; (bool) $showAll => 'all',
} elseif ($showAll) { (bool) $jsonMode => 'json',
$mode = 'all'; default => 'field',
} elseif ($jsonMode) { };
$mode = 'json';
} else { $root = realpath($path) ?: $path;
$mode = 'field';
// ── 1. Resolve org/repo ──────────────────────────────────────────
[$org, $repo] = $this->resolveOrgRepo($root);
// ── 2. Primary: Gitea manifest API ───────────────────────────────
$fields = null;
if ($org !== '' && $repo !== '') {
$fields = $this->fetchFromApi($org, $repo);
} }
// -- Locate manifest -- // ── 3. Fallback: auto-detect from source tree ────────────────────
$root = realpath($path) ?: $path; if ($fields === null) {
$manifestFile = null; $this->log('INFO', 'API unavailable — falling back to source-tree detection');
$fields = $this->autoDetect($root, $repo);
}
<<<<<<< HEAD
// Priority: manifest.xml (current standard) // Priority: manifest.xml (current standard)
$candidates = [ $candidates = [
"{$root}/.mokogitea/manifest.xml", "{$root}/.mokogitea/manifest.xml",
"{$root}/.mokogitea/.manifest.xml", // legacy (dot-prefixed) "{$root}/.mokogitea/.manifest.xml", // legacy (dot-prefixed)
"{$root}/.mokogitea/.moko-platform", // legacy v4 "{$root}/.mokogitea/.MokoCLI", // legacy v4
=======
if (empty($fields)) {
$this->log('ERROR', "Could not resolve metadata for {$root}");
return 1;
}
// Provide backward-compatible aliases (hyphenated → underscore)
$fields = $this->addAliases($fields);
// Strip empty values
$fields = array_filter($fields, fn($v) => $v !== '' && $v !== null);
// ── 4. Output ────────────────────────────────────────────────────
return $this->outputFields($fields, $mode, $field);
}
// ── Gitea manifest API ───────────────────────────────────────────────
private function fetchFromApi(string $org, string $repo): ?array
{
$token = getenv('GA_TOKEN') ?: getenv('GITEA_TOKEN') ?: '';
$baseUrl = getenv('GITEA_URL') ?: 'https://git.mokoconsulting.tech';
$baseUrl = rtrim($baseUrl, '/');
if ($token === '') {
return null;
}
$url = "{$baseUrl}/api/v1/repos/{$org}/{$repo}/manifest";
$ctx = stream_context_create([
'http' => [
'header' => "Authorization: token {$token}\r\nAccept: application/json\r\n",
'timeout' => 10,
'ignore_errors' => true,
],
]);
$body = @file_get_contents($url, false, $ctx);
if ($body === false) {
return null;
}
// Check HTTP status from response headers
$status = 0;
if (isset($http_response_header[0])) {
preg_match('/\d{3}/', $http_response_header[0], $m);
$status = (int) ($m[0] ?? 0);
}
if ($status < 200 || $status >= 300) {
return null;
}
$data = json_decode($body, true);
if (!is_array($data) || empty($data)) {
return null;
}
$this->log('INFO', "Loaded metadata from Gitea manifest API ({$org}/{$repo})");
return $data;
}
// ── Auto-detection fallback ──────────────────────────────────────────
private function autoDetect(string $root, string $repoName): array
{
$fields = [
'name' => $repoName ?: basename($root),
'org' => 'MokoConsulting',
>>>>>>> main
]; ];
foreach ($candidates as $candidate) { // Resolve source directory (source/ or src/)
if (file_exists($candidate)) { $srcDir = null;
$manifestFile = $candidate; foreach (['source', 'src'] as $candidate) {
if (is_dir("{$root}/{$candidate}")) {
$srcDir = $candidate;
break; break;
} }
} }
if ($manifestFile === null) { // ── Try Joomla detection ─────────────────────────────────────
$this->log('ERROR', "No manifest found in {$root}"); $joomlaResult = $this->detectJoomla($root, $srcDir);
return 1; if ($joomlaResult !== null) {
$fields = array_merge($fields, $joomlaResult);
$this->log('INFO', "Auto-detected platform: joomla ({$fields['extension_type']}{$fields['element_name']})");
return $fields;
} }
// -- Parse XML -- // ── Try Dolibarr detection ───────────────────────────────────
$xml = @simplexml_load_file($manifestFile); $dolibarrResult = $this->detectDolibarr($root);
if ($dolibarrResult !== null) {
$fields = array_merge($fields, $dolibarrResult);
$this->log('INFO', "Auto-detected platform: dolibarr");
return $fields;
}
if ($xml === false) { // ── Generic fallback ─────────────────────────────────────────
// Fallback: try YAML format (.mokostandards legacy) $fields['platform'] = $this->detectGenericPlatform($root);
$content = file_get_contents($manifestFile); $fields['element_name'] = strtolower($fields['name']);
$fields = []; $fields['extension_type'] = 'application';
if (preg_match('/^platform:\s*(.+)/m', $content, $m)) { $fields['language'] = $this->detectLanguage($root);
$fields['platform'] = trim($m[1], " \t\n\r\"'"); if ($srcDir !== null) {
$fields['entry_point'] = "{$srcDir}/";
}
$this->log('INFO', "Auto-detected platform: {$fields['platform']}");
return $fields;
}
/**
* Detect Joomla platform by scanning for extension XML manifests.
*
* Searches root and source/ dirs for XML files containing <extension type="...">.
* Extracts element name from the filename (pkg_*, com_*, mod_*, plg_*, tpl_*) or
* from the <element> tag inside the manifest.
*/
private function detectJoomla(string $root, ?string $srcDir): ?array
{
$searchDirs = [$root];
if ($srcDir !== null) {
$searchDirs[] = "{$root}/{$srcDir}";
}
foreach ($searchDirs as $dir) {
$xmlFiles = glob("{$dir}/*.xml") ?: [];
foreach ($xmlFiles as $xmlFile) {
$content = @file_get_contents($xmlFile);
if ($content === false) {
continue;
}
// Match <extension type="component|module|plugin|package|template|file|library">
if (!preg_match('/<extension\s+[^>]*type="([^"]+)"/', $content, $typeMatch)) {
// Also try legacy <install type="...">
if (!preg_match('/<install\s+[^>]*type="([^"]+)"/', $content, $typeMatch)) {
continue;
}
}
$extType = strtolower($typeMatch[1]);
$basename = pathinfo($xmlFile, PATHINFO_FILENAME);
// Try to extract element name from XML <element> tag
$xml = @simplexml_load_string($content);
$element = '';
if ($xml !== false) {
// Package manifests have <files><file ...>element</file></files>
// Component/module manifests have <element> or use filename
$element = (string) ($xml->element ?? '');
if ($element === '') {
$element = strtolower($basename);
}
} else {
$element = strtolower($basename);
}
// Derive display name
$displayName = (string) ($xml->name ?? ucfirst(str_replace('_', ' ', $basename)));
return [
'platform' => 'joomla',
'extension_type' => $extType,
'element_name' => $element,
'display_name' => $displayName,
'language' => 'PHP',
'entry_point' => ($srcDir ?? '.') . '/',
];
} }
if (preg_match('/^standards_version:\s*(.+)/m', $content, $m)) {
$fields['standards-version'] = trim($m[1], " \t\n\r\"'"); // Also check for pkg_*.xml pattern specifically
$pkgFiles = glob("{$dir}/pkg_*.xml") ?: [];
if (!empty($pkgFiles)) {
$basename = pathinfo($pkgFiles[0], PATHINFO_FILENAME);
return [
'platform' => 'joomla',
'extension_type' => 'package',
'element_name' => strtolower($basename),
'display_name' => ucfirst(str_replace('_', ' ', $basename)),
'language' => 'PHP',
'entry_point' => ($srcDir ?? '.') . '/',
];
} }
if (preg_match('/^governed_repo:\s*(.+)/m', $content, $m)) { }
$fields['name'] = trim($m[1], " \t\n\r\"'");
// Check for com_*/manifest.xml pattern (component subdirectory)
$comDirs = glob("{$root}/com_*", GLOB_ONLYDIR) ?: [];
foreach ($comDirs as $comDir) {
$comManifest = glob("{$comDir}/*.xml") ?: [];
foreach ($comManifest as $xmlFile) {
$content = @file_get_contents($xmlFile);
if ($content && preg_match('/<extension\s+[^>]*type="component"/', $content)) {
return [
'platform' => 'joomla',
'extension_type' => 'component',
'element_name' => strtolower(basename($comDir)),
'display_name' => ucfirst(str_replace('com_', '', basename($comDir))),
'language' => 'PHP',
'entry_point' => ($srcDir ?? '.') . '/',
];
}
} }
} else { }
// Register namespace for XPath (optional, simple path works without)
$fields = [ return null;
'name' => (string)($xml->identity->name ?? ''), }
'display-name' => (string)($xml->identity->{"display-name"} ?? ''),
'org' => (string)($xml->identity->org ?? ''), /**
'description' => (string)($xml->identity->description ?? ''), * Detect Dolibarr platform by scanning for module descriptor files.
'license' => (string)($xml->identity->license ?? ''), */
'license-spdx' => (string)($xml->identity->license['spdx'] ?? ''), private function detectDolibarr(string $root): ?array
'platform' => (string)($xml->governance->platform ?? ''), {
'standards-version' => (string)($xml->governance->{"standards-version"} ?? ''), // Look for mod*.class.php containing DolibarrModules
'standards-source' => (string)($xml->governance->{"standards-source"} ?? ''), $searchPaths = [
'language' => (string)($xml->build->language ?? ''), "{$root}/core/modules/mod*.class.php",
'package-type' => (string)($xml->build->{"package-type"} ?? ''), "{$root}/*/core/modules/mod*.class.php",
'entry-point' => (string)($xml->build->{"entry-point"} ?? ''), ];
'version' => (string)($xml->identity->version ?? ''),
'source-dir' => (string)($xml->deploy->{"source-dir"} ?? ''), foreach ($searchPaths as $pattern) {
'remote-subdir' => (string)($xml->deploy->{"remote-subdir"} ?? ''), $files = glob($pattern) ?: [];
'excludes' => (string)($xml->deploy->excludes ?? ''), foreach ($files as $file) {
'dev-host' => (string)($xml->deploy->{"dev-host"} ?? ''), $content = @file_get_contents($file);
'demo-host' => (string)($xml->deploy->{"demo-host"} ?? ''), if ($content && str_contains($content, 'DolibarrModules')) {
'manifest-file' => $manifestFile, $modName = pathinfo($file, PATHINFO_FILENAME);
// modMyModule.class → mymodule
$element = strtolower(preg_replace('/^mod/', '', str_replace('.class', '', $modName)));
return [
'platform' => 'dolibarr',
'extension_type' => 'module',
'element_name' => $element,
'display_name' => ucfirst($element),
'language' => 'PHP',
'entry_point' => './',
];
}
}
}
// Secondary: check for update.txt (Dolibarr marker)
if (file_exists("{$root}/update.txt")) {
return [
'platform' => 'dolibarr',
'extension_type' => 'module',
'element_name' => strtolower(basename($root)),
'display_name' => basename($root),
'language' => 'PHP',
'entry_point' => './',
]; ];
} }
// Strip empty values for cleaner output return null;
$fields = array_filter($fields, fn($v) => $v !== ''); }
// -- Output -- /**
* Detect generic platform type (php, nodejs, python, etc.) from project files.
*/
private function detectGenericPlatform(string $root): string
{
if (file_exists("{$root}/composer.json")) {
return 'php';
}
if (file_exists("{$root}/package.json")) {
return 'nodejs';
}
if (file_exists("{$root}/pyproject.toml") || file_exists("{$root}/setup.py")) {
return 'python';
}
if (file_exists("{$root}/go.mod")) {
return 'go';
}
if (file_exists("{$root}/Cargo.toml")) {
return 'rust';
}
return 'generic';
}
/**
* Detect primary language from project files.
*/
private function detectLanguage(string $root): string
{
if (file_exists("{$root}/composer.json")) {
return 'PHP';
}
if (file_exists("{$root}/tsconfig.json")) {
return 'TypeScript';
}
if (file_exists("{$root}/package.json")) {
return 'JavaScript';
}
if (file_exists("{$root}/pyproject.toml") || file_exists("{$root}/setup.py")) {
return 'Python';
}
return '';
}
// ── Org/repo resolution ──────────────────────────────────────────────
/**
* Resolve org and repo name from environment or git remote.
*
* @return array{0: string, 1: string} [org, repo]
*/
private function resolveOrgRepo(string $root): array
{
// 1. GITHUB_REPOSITORY env (set in Gitea Actions / GitHub Actions)
$envRepo = getenv('GITHUB_REPOSITORY') ?: '';
if ($envRepo !== '' && str_contains($envRepo, '/')) {
return explode('/', $envRepo, 2);
}
// 2. Parse git remote origin URL
$remoteUrl = trim((string) shell_exec(
'git -C ' . escapeshellarg($root) . ' remote get-url origin 2>/dev/null'
));
if ($remoteUrl !== '') {
// SSH: git@host:Org/Repo.git or HTTPS: https://host/Org/Repo.git
if (preg_match('#[/:]([^/]+)/([^/]+?)(?:\.git)?$#', $remoteUrl, $m)) {
return [$m[1], $m[2]];
}
}
return ['', basename($root)];
}
// ── Backward-compatible aliases ──────────────────────────────────────
/**
* Add hyphenated aliases for underscore fields (backward compat with old manifest.xml consumers).
* Also map old field names to new ones.
*/
private function addAliases(array $fields): array
{
// Map API field names → old manifest.xml hyphenated names
$aliases = [
'display_name' => 'display-name',
'license_spdx' => 'license-spdx',
'license_name' => 'license',
'standards_version' => 'standards-version',
'standards_source' => 'standards-source',
'extension_type' => 'package-type',
'entry_point' => 'entry-point',
'element_name' => 'name',
];
foreach ($aliases as $newKey => $oldKey) {
if (isset($fields[$newKey]) && !isset($fields[$oldKey])) {
$fields[$oldKey] = $fields[$newKey];
}
}
return $fields;
}
// ── Output ───────────────────────────────────────────────────────────
private function outputFields(array $fields, string $mode, string $field): int
{
switch ($mode) { switch ($mode) {
case 'field': case 'field':
if ($field === '') { if ($field === '') {
$this->log('ERROR', "Usage: manifest_read.php --path <dir> --field <name>"); $this->log('ERROR', "Usage: manifest:read --path <dir> --field <name>");
$this->log('ERROR', " manifest_read.php --path <dir> --all"); $this->log('ERROR', " manifest:read --path <dir> --all");
$this->log('ERROR', " manifest_read.php --path <dir> --json"); $this->log('ERROR', " manifest:read --path <dir> --json");
$this->log('ERROR', " manifest_read.php --path <dir> --github-output"); $this->log('ERROR', " manifest:read --path <dir> --github-output");
return 2; return 2;
} }
echo ($fields[$field] ?? '') . "\n"; echo ($fields[$field] ?? '') . "\n";
@@ -142,22 +464,21 @@ class ManifestReadCli extends CliFramework
break; break;
case 'github-output': case 'github-output':
$outputFile = getenv('GITHUB_OUTPUT'); $outputFile = getenv('GITHUB_OUTPUT') ?: getenv('GITEA_OUTPUT') ?: '';
if ($outputFile === false || $outputFile === '') { $lines = [];
$this->log('ERROR', 'GITHUB_OUTPUT not set — printing to stdout instead'); foreach ($fields as $k => $v) {
foreach ($fields as $k => $v) { $envKey = str_replace('-', '_', $k);
// Convert field-name to FIELD_NAME for env var style $lines[$envKey] = "{$envKey}={$v}\n";
$envKey = str_replace('-', '_', $k); }
echo "{$envKey}={$v}\n"; // Deduplicate (aliases may collide after underscore conversion)
} $output = implode('', $lines);
if ($outputFile === '') {
$this->log('WARNING', 'GITHUB_OUTPUT not set — printing to stdout');
echo $output;
} else { } else {
$fh = fopen($outputFile, 'a'); file_put_contents($outputFile, $output, FILE_APPEND);
foreach ($fields as $k => $v) { $this->log('INFO', "Wrote " . count($lines) . " fields to GITHUB_OUTPUT");
$envKey = str_replace('-', '_', $k);
fwrite($fh, "{$envKey}={$v}\n");
}
fclose($fh);
$this->log('INFO', "Wrote " . count($fields) . " fields to GITHUB_OUTPUT");
} }
break; break;
} }
+13 -12
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/package_build.php * PATH: /cli/package_build.php
* BRIEF: Build ZIP and tar.gz install packages for Joomla/Dolibarr/generic projects * BRIEF: Build ZIP and tar.gz install packages for Joomla/Dolibarr/generic projects
* *
@@ -19,7 +25,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\{CliFramework, SourceResolver};
class PackageBuildCli extends CliFramework class PackageBuildCli extends CliFramework
{ {
@@ -56,18 +62,13 @@ class PackageBuildCli extends CliFramework
} }
// -- Determine source directory ----------------------------------------------- // -- Determine source directory -----------------------------------------------
$sourceDir = null; $sourceDir = SourceResolver::resolveAbsolute($root);
foreach (['src', 'htdocs'] as $candidate) {
if (is_dir("{$root}/{$candidate}")) {
$sourceDir = "{$root}/{$candidate}";
break;
}
}
if ($sourceDir === null) { if ($sourceDir === null) {
$this->log('ERROR', "No src/ or htdocs/ directory found in {$root}"); $this->log('ERROR', "No source/ or src/ directory found in {$root}");
return 1; return 1;
} }
SourceResolver::warnIfLegacy($root);
// -- Determine element and type prefix from manifest -------------------------- // -- Determine element and type prefix from manifest --------------------------
$extElement = $elementOverride; $extElement = $elementOverride;
+171 -20
View File
@@ -6,25 +6,40 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/platform_detect.php * PATH: /cli/platform_detect.php
* BRIEF: Detect platform from manifest.xml file — outputs platform string * VERSION: 09.25.05
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/platform_detect.php
* VERSION: 09.32.00
>>>>>>> main
* BRIEF: Auto-detect repository platform type and optionally update manifest
*/ */
declare(strict_types=1); declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class PlatformDetectCli extends CliFramework class PlatformDetectCli extends CliFramework
{ {
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Detect platform from manifest.xml file'); $this->setDescription('Auto-detect repository platform type and optionally update manifest');
$this->addArgument('--path', 'Repository root path', '.'); $this->addArgument('--path', 'Local repo path to scan (default: .)', '.');
$this->addArgument('--token', 'Gitea API token for updating manifest', '');
$this->addArgument('--gitea-url', 'Gitea URL (default: https://git.mokoconsulting.tech)', 'https://git.mokoconsulting.tech');
$this->addArgument('--owner', 'Repo owner for API update', '');
$this->addArgument('--repo', 'Repo name for API update', '');
$this->addArgument('--update', 'Update manifest.platform via API (flag)', 'false');
$this->addArgument('--github-output', 'Append platform=xxx to $GITHUB_OUTPUT (flag)', 'false');
} }
protected function run(): int protected function run(): int
@@ -32,25 +47,161 @@ class PlatformDetectCli extends CliFramework
$path = $this->getArgument('--path'); $path = $this->getArgument('--path');
$root = realpath($path) ?: $path; $root = realpath($path) ?: $path;
// Check .mokogitea/manifest.xml first, fallback to root $token = $this->getArgument('--token');
$file = "{$root}/.mokogitea/manifest.xml"; $giteaUrl = rtrim($this->getArgument('--gitea-url'), '/');
if (!file_exists($file)) { $owner = $this->getArgument('--owner');
$file = "{$root}/.mokostandards"; $repo = $this->getArgument('--repo');
} $doUpdate = $this->isFlagSet('--update');
if (!file_exists($file)) { $githubOutput = $this->isFlagSet('--github-output');
echo "unknown\n";
return 0; $platform = $this->detectPlatform($root);
$this->log('INFO', "Detected platform: {$platform}");
echo $platform . "\n";
// Append to $GITHUB_OUTPUT if requested
if ($githubOutput) {
$outputFile = getenv('GITHUB_OUTPUT');
if ($outputFile !== false && $outputFile !== '') {
file_put_contents($outputFile, "platform={$platform}\n", FILE_APPEND);
$this->log('INFO', "Appended platform={$platform} to \$GITHUB_OUTPUT");
} else {
$this->log('WARN', '$GITHUB_OUTPUT is not set; skipping output append.');
}
} }
$content = file_get_contents($file); // Update manifest via API if requested
if (preg_match('/^platform:\s*(.+)/m', $content, $m)) { if ($doUpdate) {
echo trim($m[1], " \t\n\r\"'") . "\n"; if ($token === '' || $owner === '' || $repo === '') {
} else { $this->log('ERROR', '--update requires --token, --owner, and --repo.');
echo "unknown\n"; return 1;
}
if ($this->dryRun) {
$this->log('INFO', "[DRY RUN] Would update manifest.platform to \"{$platform}\" "
. "for {$owner}/{$repo}.");
return 0;
}
$this->log('INFO', "Updating manifest.platform for {$owner}/{$repo} to \"{$platform}\"...");
$response = $this->apiRequest(
$giteaUrl,
$token,
'PATCH',
"/api/v1/repos/{$owner}/{$repo}/metadata",
json_encode(['platform' => $platform])
);
if ($response['code'] >= 200 && $response['code'] < 300) {
$this->log('INFO', "Manifest updated successfully (HTTP {$response['code']}).");
} else {
$this->log('ERROR', "Failed to update manifest (HTTP {$response['code']}): "
. $response['body']);
return 1;
}
} }
return 0; return 0;
} }
private function detectPlatform(string $root): string
{
// 1. Joomla — has pkg_*.xml or Joomla-style extension manifest
$joomlaIndicators = array_merge(
glob("{$root}/source/pkg_*.xml") ?: [],
glob("{$root}/pkg_*.xml") ?: [],
glob("{$root}/source/packages/*/services/provider.php") ?: [],
glob("{$root}/**/templateDetails.xml") ?: [],
);
if (!empty($joomlaIndicators)) {
return 'joomla';
}
// 2. Dolibarr — has mod*.class.php or dolibarr module descriptor
$doliIndicators = array_merge(
glob("{$root}/core/modules/mod*.class.php") ?: [],
glob("{$root}/class/*.class.php") ?: [],
);
if (!empty($doliIndicators) && file_exists("{$root}/langs")) {
return 'dolibarr';
}
// 3. Go — has go.mod
if (file_exists("{$root}/go.mod")) {
return 'go';
}
// 4. MCP — has package.json with mcp-related content or dist/index.js pattern
if (file_exists("{$root}/package.json")) {
$pkg = json_decode(file_get_contents("{$root}/package.json"), true);
$name = $pkg['name'] ?? '';
if (str_contains($name, 'mcp') || isset($pkg['dependencies']['@modelcontextprotocol/sdk'])) {
return 'mcp';
}
}
// 5. Platform — is MokoCLI itself or org-config
$repoName = basename($root);
if (in_array($repoName, ['mokocli', 'mokoplatform', 'mokogitea-org-config'])) {
return 'platform';
}
// 6. Default
return 'generic';
}
private function isFlagSet(string $flag): bool
{
$value = $this->getArgument($flag);
return $value === 'true' || $value === '1' || $value === 'yes';
}
private function apiRequest(
string $giteaUrl,
string $token,
string $method,
string $endpoint,
?string $body = null
): array {
$url = $giteaUrl . $endpoint;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Accept: application/json',
"Authorization: token {$token}",
]);
if ($body !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
}
$responseBody = curl_exec($ch);
$httpCode = (int) curl_getinfo(
$ch,
CURLINFO_HTTP_CODE
);
if (curl_errno($ch)) {
$error = curl_error($ch);
curl_close($ch);
return [
'code' => 0,
'body' => "cURL error: {$error}",
];
}
curl_close($ch);
return ['code' => $httpCode, 'body' => $responseBody];
}
} }
$app = new PlatformDetectCli(); $app = new PlatformDetectCli();
+18 -6
View File
@@ -6,24 +6,36 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/release.php * PATH: /cli/release.php
* BRIEF: Automate the moko-platform version branch release flow * BRIEF: Automate the MokoCLI version branch release flow
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/release.php
* BRIEF: Automate the mokoplatform version branch release flow
>>>>>>> main
*/ */
declare(strict_types=1); declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class ReleaseCli extends CliFramework class ReleaseCli extends CliFramework
{ {
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Automate the moko-platform version branch release flow'); <<<<<<< HEAD
$this->setDescription('Automate the MokoCLI version branch release flow');
=======
$this->setDescription('Automate the mokoplatform version branch release flow');
>>>>>>> main
$this->addArgument('--bump', 'Bump type: patch, minor, or major', ''); $this->addArgument('--bump', 'Bump type: patch, minor, or major', '');
} }
+10 -4
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/release_body_update.php * PATH: /cli/release_body_update.php
* BRIEF: Update Gitea release body with changelog extract and checksums * BRIEF: Update Gitea release body with changelog extract and checksums
*/ */
@@ -17,7 +23,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class ReleaseBodyUpdateCli extends CliFramework class ReleaseBodyUpdateCli extends CliFramework
{ {
+322 -8
View File
@@ -6,31 +6,345 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/release_cascade.php * PATH: /cli/release_cascade.php
* VERSION: 09.23.00 * VERSION: 09.25.05
* BRIEF: DEPRECATED — cascade behavior removed. Each release stream is independent. * BRIEF: DEPRECATED — cascade behavior removed. Each release stream is independent.
=======
* DEFGROUP: mokocli.CLI
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/release_cascade.php
* VERSION: 09.32.00
* BRIEF: Cascade release zip to all lower stability channels
>>>>>>> main
*/ */
declare(strict_types=1); declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class ReleaseCascadeCli extends CliFramework class ReleaseCascadeCli extends CliFramework
{ {
/** Channel hierarchy: highest stability first. */
private const CHANNELS = ['stable', 'release-candidate', 'beta', 'alpha', 'development'];
/** Map stability input names to canonical tag names. */
private const TAG_MAP = [
'stable' => 'stable',
'release-candidate' => 'release-candidate',
'rc' => 'release-candidate',
'beta' => 'beta',
'alpha' => 'alpha',
'development' => 'development',
'dev' => 'development',
];
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('DEPRECATED — cascade behavior removed'); $this->setDescription('Cascade release zip to all lower stability channels');
$this->addArgument('--stability', 'Source stability channel (required)', '');
$this->addArgument('--token', 'Gitea API token (required)', '');
$this->addArgument('--api-base', 'Gitea API base URL for the repo (required)', '');
} }
protected function run(): int protected function run(): int
{ {
$this->log('INFO', 'No-op (cascade behavior removed — each stream is independent)'); $stability = strtolower($this->getArgument('--stability'));
return 0; $token = $this->getArgument('--token');
$apiBase = rtrim($this->getArgument('--api-base'), '/');
if ($token === '') {
$envToken = getenv('MOKOGITEA_TOKEN');
if ($envToken === false || $envToken === '') {
$envToken = getenv('GITEA_TOKEN');
}
if ($envToken !== false && $envToken !== '') {
$token = $envToken;
}
}
if ($stability === '' || $token === '' || $apiBase === '') {
$this->log('ERROR', 'Usage: release_cascade.php --stability CHANNEL --token TOKEN --api-base URL');
return 1;
}
$sourceTag = self::TAG_MAP[$stability] ?? null;
if ($sourceTag === null) {
$this->log('ERROR', "Unknown stability: {$stability}");
return 1;
}
// Find lower channels to cascade to
$lowerChannels = $this->getLowerChannels($sourceTag);
if (count($lowerChannels) === 0) {
$this->log('INFO', "No lower channels for '{$stability}' — nothing to cascade.");
return 0;
}
$this->log('INFO', "Cascading from '{$sourceTag}' to: " . implode(', ', $lowerChannels));
if ($this->dryRun) {
$this->log('INFO', '[DRY RUN] No changes will be made.');
}
// 1. Get source release
$sourceRelease = $this->giteaApi("{$apiBase}/releases/tags/{$sourceTag}", $token);
if ($sourceRelease === null) {
$this->log('WARN', "No release found at tag '{$sourceTag}' — nothing to cascade.");
return 0;
}
$sourceVersion = $sourceRelease['name'] ?? $sourceTag;
$sourceBody = $sourceRelease['body'] ?? '';
$sourceAssets = $sourceRelease['assets'] ?? [];
// Find zip assets (exclude .sha256 sidecars)
$zipAssets = array_filter($sourceAssets, function (array $asset): bool {
$name = strtolower($asset['name'] ?? '');
return str_ends_with($name, '.zip') && !str_ends_with($name, '.sha256');
});
// Also grab sha256 sidecars
$sha256Assets = array_filter($sourceAssets, function (array $asset): bool {
return str_ends_with(strtolower($asset['name'] ?? ''), '.zip.sha256');
});
if (count($zipAssets) === 0) {
$this->log('WARN', "Source release '{$sourceTag}' has no zip assets — nothing to cascade.");
return 0;
}
$this->log('INFO', "Source: {$sourceVersion}" . count($zipAssets) . " zip(s)");
echo "\n";
// 2. Download source assets to temp files
$downloads = [];
foreach (array_merge($zipAssets, $sha256Assets) as $asset) {
$url = $asset['browser_download_url'] ?? '';
if ($url === '') {
continue;
}
$tmpFile = tempnam(sys_get_temp_dir(), 'cascade_');
if ($this->downloadFile($url, $token, $tmpFile)) {
$downloads[] = ['name' => $asset['name'], 'path' => $tmpFile];
$this->log('INFO', "Downloaded: {$asset['name']}");
} else {
$this->log('ERROR', "Failed to download: {$asset['name']}");
}
}
if (count($downloads) === 0) {
$this->log('ERROR', 'Could not download any source assets.');
return 1;
}
// 3. Cascade to each lower channel
$errors = 0;
foreach ($lowerChannels as $targetTag) {
echo "\n";
$result = $this->cascadeToChannel(
$apiBase, $token, $targetTag,
$sourceVersion, $sourceBody, $downloads
);
if (!$result) {
$errors++;
}
}
// 4. Cleanup temp files
foreach ($downloads as $dl) {
@unlink($dl['path']);
}
echo "\n";
$this->log('INFO', "Cascade complete. " . (count($lowerChannels) - $errors)
. "/" . count($lowerChannels) . " channels updated.");
return $errors > 0 ? 1 : 0;
}
/**
* Cascade assets to a single target channel.
*/
private function cascadeToChannel(
string $apiBase,
string $token,
string $targetTag,
string $sourceVersion,
string $sourceBody,
array $downloads
): bool {
$this->log('INFO', "{$targetTag}");
if ($this->dryRun) {
$this->log('INFO', " [DRY RUN] Would cascade to {$targetTag}");
return true;
}
// Find existing release at target tag
$existing = $this->giteaApi("{$apiBase}/releases/tags/{$targetTag}", $token);
if ($existing !== null && !empty($existing['id'])) {
$releaseId = (int) $existing['id'];
// Delete existing assets
$existingAssets = $existing['assets'] ?? [];
foreach ($existingAssets as $asset) {
$assetId = $asset['id'] ?? 0;
if ($assetId > 0) {
$this->giteaApi(
"{$apiBase}/releases/{$releaseId}/assets/{$assetId}",
$token, 'DELETE'
);
}
}
// Update release metadata
$updatePayload = json_encode([
'name' => $sourceVersion,
'body' => $sourceBody,
]);
$this->giteaApi(
"{$apiBase}/releases/{$releaseId}",
$token, 'PATCH', $updatePayload
);
$this->log('INFO', " Updated release metadata (id: {$releaseId})");
} else {
// Create new release at target tag
// Use the source release's target commitish so the tag points to the same commit
$createPayload = json_encode([
'tag_name' => $targetTag,
'target_commitish' => 'main',
'name' => $sourceVersion,
'body' => $sourceBody,
'prerelease' => ($targetTag !== 'stable'),
]);
$newRelease = $this->giteaApi("{$apiBase}/releases", $token, 'POST', $createPayload);
if ($newRelease === null || empty($newRelease['id'])) {
$this->log('ERROR', " Failed to create release at tag '{$targetTag}'");
return false;
}
$releaseId = (int) $newRelease['id'];
$this->log('INFO', " Created release (id: {$releaseId})");
}
// Upload assets
foreach ($downloads as $dl) {
$uploadUrl = "{$apiBase}/releases/{$releaseId}/assets?name=" . rawurlencode($dl['name']);
$success = $this->uploadAsset($uploadUrl, $token, $dl['path'], $dl['name']);
if ($success) {
$this->log('INFO', " Uploaded: {$dl['name']}");
} else {
$this->log('ERROR', " Failed to upload: {$dl['name']}");
}
}
return true;
}
/**
* Get all channels below the given source channel.
*/
private function getLowerChannels(string $sourceTag): array
{
$idx = array_search($sourceTag, self::CHANNELS, true);
if ($idx === false) {
return [];
}
return array_slice(self::CHANNELS, $idx + 1);
}
/**
* Download a file via HTTP.
*/
private function downloadFile(string $url, string $token, string $destPath): bool
{
$ch = curl_init($url);
if ($ch === false) {
return false;
}
$fp = fopen($destPath, 'wb');
if ($fp === false) {
return false;
}
curl_setopt_array($ch, [
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_FILE => $fp,
CURLOPT_HTTPHEADER => ["Authorization: token {$token}"],
CURLOPT_TIMEOUT => 120,
]);
curl_exec($ch);
$code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
fclose($fp);
return $code >= 200 && $code < 300;
}
/**
* Upload a file as a release asset via multipart form.
*/
private function uploadAsset(string $url, string $token, string $filePath, string $fileName): bool
{
$ch = curl_init($url);
if ($ch === false) {
return false;
}
$cfile = new CURLFile($filePath, 'application/octet-stream', $fileName);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => ['attachment' => $cfile],
CURLOPT_HTTPHEADER => ["Authorization: token {$token}"],
CURLOPT_TIMEOUT => 120,
]);
$response = curl_exec($ch);
$code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return $code >= 200 && $code < 300;
}
/**
* Make an HTTP request to the Gitea API.
*/
private function giteaApi(
string $url,
string $token,
string $method = 'GET',
?string $body = null
): ?array {
$ch = curl_init($url);
if ($ch === false) {
return null;
}
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: token {$token}",
'Content-Type: application/json',
],
CURLOPT_TIMEOUT => 30,
CURLOPT_CUSTOMREQUEST => $method,
]);
if ($body !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
}
$response = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode < 200 || $httpCode >= 300 || empty($response) || !is_string($response)) {
return null;
}
$decoded = json_decode($response, true);
return is_array($decoded) ? $decoded : null;
} }
} }
+13 -8
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/release_create.php * PATH: /cli/release_create.php
* BRIEF: Create or overwrite a Gitea release with proper naming * BRIEF: Create or overwrite a Gitea release with proper naming
*/ */
@@ -17,7 +23,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\{CliFramework, SourceResolver};
class ReleaseCreateCli extends CliFramework class ReleaseCreateCli extends CliFramework
{ {
@@ -97,8 +103,8 @@ class ReleaseCreateCli extends CliFramework
// Find extension manifest (Joomla XML) // Find extension manifest (Joomla XML)
$extManifest = null; $extManifest = null;
$manifestFiles = array_merge( $manifestFiles = array_merge(
glob("{$root}/src/pkg_*.xml") ?: [], SourceResolver::globSource($root, 'pkg_*.xml'),
glob("{$root}/src/*.xml") ?: [], SourceResolver::globSource($root, '*.xml'),
glob("{$root}/*.xml") ?: [] glob("{$root}/*.xml") ?: []
); );
foreach ($manifestFiles as $file) { foreach ($manifestFiles as $file) {
@@ -112,8 +118,7 @@ class ReleaseCreateCli extends CliFramework
// Find Dolibarr module file // Find Dolibarr module file
$modFile = null; $modFile = null;
$modFiles = array_merge( $modFiles = array_merge(
glob("{$root}/src/core/modules/mod*.class.php") ?: [], SourceResolver::globSource($root, 'core/modules/mod*.class.php'),
glob("{$root}/htdocs/core/modules/mod*.class.php") ?: [],
glob("{$root}/core/modules/mod*.class.php") ?: [] glob("{$root}/core/modules/mod*.class.php") ?: []
); );
foreach ($modFiles as $file) { foreach ($modFiles as $file) {
+10 -4
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/release_manage.php * PATH: /cli/release_manage.php
* BRIEF: Create/update Gitea releases, upload assets, update release body * BRIEF: Create/update Gitea releases, upload assets, update release body
*/ */
@@ -17,7 +23,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class ReleaseManageCli extends CliFramework class ReleaseManageCli extends CliFramework
{ {
+20 -6
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/release_mirror.php * PATH: /cli/release_mirror.php
* BRIEF: Mirror a Gitea release (with assets) to a GitHub repository * BRIEF: Mirror a Gitea release (with assets) to a GitHub repository
*/ */
@@ -17,7 +23,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class ReleaseMirrorCli extends CliFramework class ReleaseMirrorCli extends CliFramework
{ {
@@ -201,7 +207,11 @@ class ReleaseMirrorCli extends CliFramework
CURLOPT_HTTPHEADER => [ CURLOPT_HTTPHEADER => [
"Authorization: token {$token}", "Authorization: token {$token}",
'Accept: application/vnd.github+json', 'Accept: application/vnd.github+json',
'User-Agent: moko-platform', <<<<<<< HEAD
'User-Agent: MokoCLI',
=======
'User-Agent: mokoplatform',
>>>>>>> main
'Content-Type: application/json', 'Content-Type: application/json',
], ],
CURLOPT_TIMEOUT => 30, CURLOPT_TIMEOUT => 30,
@@ -229,7 +239,11 @@ class ReleaseMirrorCli extends CliFramework
CURLOPT_HTTPHEADER => [ CURLOPT_HTTPHEADER => [
"Authorization: token {$token}", "Authorization: token {$token}",
'Accept: application/vnd.github+json', 'Accept: application/vnd.github+json',
'User-Agent: moko-platform', <<<<<<< HEAD
'User-Agent: MokoCLI',
=======
'User-Agent: mokoplatform',
>>>>>>> main
'Content-Type: application/octet-stream', 'Content-Type: application/octet-stream',
], ],
CURLOPT_POSTFIELDS => file_get_contents($filePath), CURLOPT_POSTFIELDS => file_get_contents($filePath),
+10 -4
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/release_notes.php * PATH: /cli/release_notes.php
* BRIEF: Extract release notes from CHANGELOG.md for a given version * BRIEF: Extract release notes from CHANGELOG.md for a given version
*/ */
@@ -17,7 +23,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class ReleaseNotesCli extends CliFramework class ReleaseNotesCli extends CliFramework
{ {
+37 -12
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/release_package.php * PATH: /cli/release_package.php
* BRIEF: Build packages (ZIP + tar.gz) with SHA-256 and upload to Gitea release * BRIEF: Build packages (ZIP + tar.gz) with SHA-256 and upload to Gitea release
*/ */
@@ -17,7 +23,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\{CliFramework, SourceResolver};
class ReleasePackageCli extends CliFramework class ReleasePackageCli extends CliFramework
{ {
@@ -99,9 +105,10 @@ class ReleasePackageCli extends CliFramework
$extFolder = ''; $extFolder = '';
$typePrefix = ''; $typePrefix = '';
SourceResolver::warnIfLegacy($root);
$manifestFiles = array_merge( $manifestFiles = array_merge(
glob("{$root}/src/pkg_*.xml") ?: [], SourceResolver::globSource($root, 'pkg_*.xml'),
glob("{$root}/src/*.xml") ?: [], SourceResolver::globSource($root, '*.xml'),
glob("{$root}/*.xml") ?: [] glob("{$root}/*.xml") ?: []
); );
@@ -200,14 +207,12 @@ class ReleasePackageCli extends CliFramework
} }
} }
if ($sourceDir === null && is_dir("{$root}/src")) { if ($sourceDir === null) {
$sourceDir = "{$root}/src"; $sourceDir = SourceResolver::resolveAbsolute($root);
} elseif ($sourceDir === null && is_dir("{$root}/htdocs")) {
$sourceDir = "{$root}/htdocs";
} }
if ($sourceDir === null) { if ($sourceDir === null) {
echo "No src/ or htdocs/ directory found — skipping package build\n"; echo "No source/ or src/ directory found — skipping package build\n";
return 0; return 0;
} }
@@ -230,12 +235,32 @@ class ReleasePackageCli extends CliFramework
$subName = basename($pkgDir); $subName = basename($pkgDir);
$subZipPath = "{$outputDir}/{$subName}.zip"; $subZipPath = "{$outputDir}/{$subName}.zip";
// If sub-package is a full repo checkout (e.g. git submodule),
// look for a source/ or src/ subdirectory containing a Joomla manifest XML
// and zip that instead of the repo root.
$subSourceDir = $pkgDir;
$subSrcAbs = SourceResolver::resolveAbsolute($pkgDir);
if ($subSrcAbs !== null) {
$srcManifests = array_merge(
glob("{$subSrcAbs}/*.xml") ?: [],
glob("{$subSrcAbs}/pkg_*.xml") ?: []
);
foreach ($srcManifests as $mf) {
if (strpos(file_get_contents($mf) ?: '', '<extension') !== false) {
$subSourceDir = $subSrcAbs;
$subSrcName = SourceResolver::resolve($pkgDir);
echo " Sub-package {$subName}: using {$subSrcName}/ entry-point\n";
break;
}
}
}
$subZip = new \ZipArchive(); $subZip = new \ZipArchive();
if ($subZip->open($subZipPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) { if ($subZip->open($subZipPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) {
$this->log('ERROR', "Failed to create sub-package ZIP: {$subZipPath}"); $this->log('ERROR', "Failed to create sub-package ZIP: {$subZipPath}");
continue; continue;
} }
$this->addDirToZip($subZip, $pkgDir, '', $this->excludePatterns); $this->addDirToZip($subZip, $subSourceDir, '', $this->excludePatterns);
$subZip->close(); $subZip->close();
$zip->addFile($subZipPath, "packages/{$subName}.zip"); $zip->addFile($subZipPath, "packages/{$subName}.zip");
+12 -6
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/release_promote.php * PATH: /cli/release_promote.php
* BRIEF: Promote a Gitea release from one channel to another (rename release, tag, assets) * BRIEF: Promote a Gitea release from one channel to another (rename release, tag, assets)
*/ */
@@ -17,7 +23,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\{CliFramework, SourceResolver};
class ReleasePromoteCli extends CliFramework class ReleasePromoteCli extends CliFramework
{ {
@@ -109,8 +115,8 @@ class ReleasePromoteCli extends CliFramework
if ($to === 'stable') { if ($to === 'stable') {
$root = realpath($path) ?: $path; $root = realpath($path) ?: $path;
$manifestFiles = array_merge( $manifestFiles = array_merge(
glob("{$root}/src/pkg_*.xml") ?: [], SourceResolver::globSource($root, 'pkg_*.xml'),
glob("{$root}/src/*.xml") ?: [], SourceResolver::globSource($root, '*.xml'),
glob("{$root}/*.xml") ?: [] glob("{$root}/*.xml") ?: []
); );
foreach ($manifestFiles as $xmlFile) { foreach ($manifestFiles as $xmlFile) {
+13 -5
View File
@@ -6,11 +6,19 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/release_publish.php * PATH: /cli/release_publish.php
* VERSION: 09.23.00 * VERSION: 09.25.05
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/release_publish.php
* VERSION: 09.32.00
>>>>>>> main
* BRIEF: Publish a release and create copies for all lesser stability streams. * BRIEF: Publish a release and create copies for all lesser stability streams.
*/ */
@@ -18,7 +26,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class ReleasePublishCli extends CliFramework class ReleasePublishCli extends CliFramework
{ {
+17 -8
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/release_validate.php * PATH: /cli/release_validate.php
* BRIEF: Pre-release validation -- version consistency, required files, manifest checks * BRIEF: Pre-release validation -- version consistency, required files, manifest checks
*/ */
@@ -17,7 +23,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\{CliFramework, SourceResolver};
class ReleaseValidateCli extends CliFramework class ReleaseValidateCli extends CliFramework
{ {
@@ -66,8 +72,10 @@ class ReleaseValidateCli extends CliFramework
$platform = 'generic'; $platform = 'generic';
} }
} }
$hasSource = is_dir("{$root}/src") || is_dir("{$root}/htdocs"); $hasSource = SourceResolver::resolveAbsolute($root) !== null;
$this->addVResult('Source directory', $hasSource ? 'PASS' : 'WARN', $hasSource ? 'src/ or htdocs/ found' : 'No src/ or htdocs/ directory'); SourceResolver::warnIfLegacy($root);
$srcDirName = SourceResolver::resolve($root);
$this->addVResult('Source directory', $hasSource ? 'PASS' : 'WARN', $hasSource ? "{$srcDirName}/ found" : 'No source/ or src/ directory');
if (!file_exists("{$root}/README.md")) { if (!file_exists("{$root}/README.md")) {
$this->addVResult('README.md', 'FAIL', 'Not found'); $this->addVResult('README.md', 'FAIL', 'Not found');
} else { } else {
@@ -109,7 +117,8 @@ class ReleaseValidateCli extends CliFramework
$this->addVResult('LICENSE', $licenseFound ? 'PASS' : 'FAIL', $licenseFound ? 'Found' : 'Not found'); $this->addVResult('LICENSE', $licenseFound ? 'PASS' : 'FAIL', $licenseFound ? 'Found' : 'Not found');
if ($platform === 'joomla') { if ($platform === 'joomla') {
$manifest = null; $manifest = null;
foreach (["{$root}/src", $root] as $dir) { $srcAbs = SourceResolver::resolveAbsolute($root);
foreach (array_filter([$srcAbs, $root]) as $dir) {
if (!is_dir($dir)) { if (!is_dir($dir)) {
continue; continue;
} foreach (glob("{$dir}/*.xml") as $xmlFile) { } foreach (glob("{$dir}/*.xml") as $xmlFile) {
@@ -156,7 +165,7 @@ class ReleaseValidateCli extends CliFramework
} }
} elseif ($platform === 'dolibarr') { } elseif ($platform === 'dolibarr') {
$modFile = null; $modFile = null;
foreach (['src', 'htdocs'] as $sd) { foreach (SourceResolver::getCandidates() as $sd) {
$matches = glob("{$root}/{$sd}/mod*.class.php"); $matches = glob("{$root}/{$sd}/mod*.class.php");
if (!empty($matches)) { if (!empty($matches)) {
$modFile = $matches[0]; $modFile = $matches[0];
+10 -4
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/release_verify.php * PATH: /cli/release_verify.php
* BRIEF: Verify a built release artifact — version, SHA256, disallowed files * BRIEF: Verify a built release artifact — version, SHA256, disallowed files
*/ */
@@ -17,7 +23,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class ReleaseVerifyCli extends CliFramework class ReleaseVerifyCli extends CliFramework
{ {
+13 -5
View File
@@ -8,11 +8,19 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/scaffold_client.php * PATH: /cli/scaffold_client.php
* VERSION: 09.23.00 * VERSION: 09.25.05
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/scaffold_client.php
* VERSION: 09.32.00
>>>>>>> main
* BRIEF: Scaffold a new client-waas repo from Template-Client-WaaS with pre-configured settings * BRIEF: Scaffold a new client-waas repo from Template-Client-WaaS with pre-configured settings
*/ */
@@ -20,7 +28,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class ScaffoldClientCli extends CliFramework class ScaffoldClientCli extends CliFramework
{ {
+17 -7
View File
@@ -8,9 +8,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/sync_rulesets.php * PATH: /cli/sync_rulesets.php
* BRIEF: Apply branch protection rules to all repos via platform adapter * BRIEF: Apply branch protection rules to all repos via platform adapter
*/ */
@@ -20,9 +26,9 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
use MokoEnterprise\Config; use MokoCli\Config;
use MokoEnterprise\PlatformAdapterFactory; use MokoCli\PlatformAdapterFactory;
class SyncRulesetsCli extends CliFramework class SyncRulesetsCli extends CliFramework
{ {
@@ -46,7 +52,11 @@ class SyncRulesetsCli extends CliFramework
); );
$platformName = $adapter->getPlatformName(); $platformName = $adapter->getPlatformName();
$ALWAYS_EXCLUDE = ['moko-platform', '.github-private']; <<<<<<< HEAD
$ALWAYS_EXCLUDE = ['MokoCLI', '.github-private'];
=======
$ALWAYS_EXCLUDE = ['mokoplatform', '.github-private'];
>>>>>>> main
// -- Protection rules (platform-agnostic format) -- // -- Protection rules (platform-agnostic format) --
$PROTECTIONS = [ $PROTECTIONS = [
+13 -12
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/theme_lint.php * PATH: /cli/theme_lint.php
* BRIEF: Lint theme files -- CSS syntax, image sizes, hardcoded URLs * BRIEF: Lint theme files -- CSS syntax, image sizes, hardcoded URLs
*/ */
@@ -17,7 +23,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\{CliFramework, SourceResolver};
class ThemeLintCli extends CliFramework class ThemeLintCli extends CliFramework
{ {
@@ -41,17 +47,12 @@ class ThemeLintCli extends CliFramework
$errors = 0; $errors = 0;
$warnings = 0; $warnings = 0;
$srcDir = null; $srcDir = SourceResolver::resolveAbsolute($root);
foreach (['src', 'htdocs'] as $d) {
if (is_dir("{$root}/{$d}")) {
$srcDir = "{$root}/{$d}";
break;
}
}
if ($srcDir === null) { if ($srcDir === null) {
$this->log('ERROR', "No src/ or htdocs/ directory in {$root}"); $this->log('ERROR', "No source/ or src/ directory in {$root}");
return 1; return 1;
} }
SourceResolver::warnIfLegacy($root);
echo "Theme Lint: {$srcDir}\n\n"; echo "Theme Lint: {$srcDir}\n\n";
+11 -5
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/updates_xml_build.php * PATH: /cli/updates_xml_build.php
* BRIEF: Generate Joomla updates.xml from extension manifest metadata * BRIEF: Generate Joomla updates.xml from extension manifest metadata
*/ */
@@ -17,7 +23,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\{CliFramework, SourceResolver};
class UpdatesXmlBuildCli extends CliFramework class UpdatesXmlBuildCli extends CliFramework
{ {
@@ -109,7 +115,7 @@ class UpdatesXmlBuildCli extends CliFramework
// -- Locate Joomla manifest --------------------------------------------------- // -- Locate Joomla manifest ---------------------------------------------------
$manifest = null; $manifest = null;
$candidates = glob("{$root}/src/pkg_*.xml") ?: []; $candidates = SourceResolver::globSource($root, 'pkg_*.xml');
foreach ($candidates as $f) { foreach ($candidates as $f) {
if (strpos(file_get_contents($f), '<extension') !== false) { if (strpos(file_get_contents($f), '<extension') !== false) {
$manifest = $f; $manifest = $f;
+13 -5
View File
@@ -6,11 +6,19 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/updates_xml_sync.php * PATH: /cli/updates_xml_sync.php
* VERSION: 09.23.00 * VERSION: 09.25.05
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/updates_xml_sync.php
* VERSION: 09.32.00
>>>>>>> main
* BRIEF: Sync updates.xml to target branches via Gitea API * BRIEF: Sync updates.xml to target branches via Gitea API
* NOTE: Called by pre-release and auto-release workflows after updates.xml * NOTE: Called by pre-release and auto-release workflows after updates.xml
* is modified on the current branch. Pushes the file to other branches * is modified on the current branch. Pushes the file to other branches
@@ -21,7 +29,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class UpdatesXmlSyncCli extends CliFramework class UpdatesXmlSyncCli extends CliFramework
{ {
+24 -8
View File
@@ -6,11 +6,19 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/version_auto_bump.php * PATH: /cli/version_auto_bump.php
* VERSION: 09.23.00 * VERSION: 09.25.05
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/version_auto_bump.php
* VERSION: 09.32.00
>>>>>>> main
* BRIEF: Auto patch-bump, set stability suffix, and commit — single CLI replacing inline workflow bash * BRIEF: Auto patch-bump, set stability suffix, and commit — single CLI replacing inline workflow bash
*/ */
@@ -18,7 +26,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\CliFramework;
class VersionAutoBumpCli extends CliFramework class VersionAutoBumpCli extends CliFramework
{ {
@@ -109,10 +117,18 @@ class VersionAutoBumpCli extends CliFramework
echo "{$line}\n"; echo "{$line}\n";
} }
// Step 2: Read version // Step 2: Read version (--quiet suppresses banner so only the version is output)
$versionOutput = []; $versionOutput = [];
exec("{$php} {$cli}/version_read.php --path " . escapeshellarg($path) . " 2>&1", $versionOutput, $versionRc); exec("{$php} {$cli}/version_read.php --path " . escapeshellarg($path) . " --quiet 2>&1", $versionOutput, $versionRc);
$version = trim($versionOutput[0] ?? ''); // Take the last non-empty line — the version is always the final output
$version = '';
foreach (array_reverse($versionOutput) as $line) {
$line = trim($line);
if (preg_match('/^\d{2}\.\d{2}\.\d{2}/', $line)) {
$version = $line;
break;
}
}
if (empty($version)) { if (empty($version)) {
echo "No version found — skipping\n"; echo "No version found — skipping\n";
+204 -35
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/version_bump.php * PATH: /cli/version_bump.php
* BRIEF: Auto-increment version -- manifest.xml is canonical, cascades to all XML and MD files * BRIEF: Auto-increment version -- manifest.xml is canonical, cascades to all XML and MD files
*/ */
@@ -17,7 +23,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\{CliFramework, SourceResolver};
class VersionBumpCli extends CliFramework class VersionBumpCli extends CliFramework
{ {
@@ -27,6 +33,7 @@ class VersionBumpCli extends CliFramework
$this->addArgument('--path', 'Repository root', '.'); $this->addArgument('--path', 'Repository root', '.');
$this->addArgument('--minor', 'Bump minor version', false); $this->addArgument('--minor', 'Bump minor version', false);
$this->addArgument('--major', 'Bump major version', false); $this->addArgument('--major', 'Bump major version', false);
$this->addArgument('--min-version', 'Minimum base version (ensures bump is above this)', '');
} }
protected function run(): int protected function run(): int
@@ -42,6 +49,7 @@ class VersionBumpCli extends CliFramework
$root = realpath($path) ?: $path; $root = realpath($path) ?: $path;
$mokoVersion = null; $mokoVersion = null;
$existingSuffix = ''; $existingSuffix = '';
$versionPrefix = '';
$mokoManifest = "{$root}/.mokogitea/manifest.xml"; $mokoManifest = "{$root}/.mokogitea/manifest.xml";
$mokoContent = ''; $mokoContent = '';
if (file_exists($mokoManifest)) { if (file_exists($mokoManifest)) {
@@ -50,29 +58,58 @@ class VersionBumpCli extends CliFramework
$mokoVersion = $m[1]; $mokoVersion = $m[1];
$existingSuffix = $m[2] ?? ''; $existingSuffix = $m[2] ?? '';
} }
// Read version_prefix from manifest.xml (supports nested and flat structure)
$xml = @simplexml_load_file($mokoManifest);
if ($xml !== false) {
$prefix = (string)($xml->identity->version_prefix ?? '');
if ($prefix === '') {
$prefix = (string)($xml->version_prefix ?? '');
}
$versionPrefix = $prefix;
}
} }
$readmeVersion = null; $readmeVersion = null;
$readme = "{$root}/README.md"; $readme = "{$root}/README.md";
$readmeContent = ''; $readmeContent = '';
if (file_exists($readme)) { if (file_exists($readme)) {
$readmeContent = file_get_contents($readme); $readmeContent = file_get_contents($readme);
if (preg_match('/VERSION:\s*(\d{2}\.\d{2}\.\d{2})/m', $readmeContent, $m)) { if (!empty($versionPrefix)) {
// Prefix-aware README scan
$prefixPattern = preg_quote($versionPrefix, '/');
if (preg_match('/' . $prefixPattern . '(\d{2}\.\d{2}\.\d{2})/m', $readmeContent, $m)) {
$readmeVersion = $m[1];
}
}
if ($readmeVersion === null && preg_match('/VERSION:\s*(\d{2}\.\d{2}\.\d{2})/m', $readmeContent, $m)) {
$readmeVersion = $m[1]; $readmeVersion = $m[1];
} }
} }
$manifestVersion = null; $manifestVersion = null;
SourceResolver::warnIfLegacy($root);
$manifestFiles = array_merge( $manifestFiles = array_merge(
glob("{$root}/src/pkg_*.xml") ?: [], SourceResolver::globSource($root, 'pkg_*.xml'),
glob("{$root}/src/*.xml") ?: [], SourceResolver::globSource($root, '*.xml'),
glob("{$root}/src/packages/*/mokowaas.xml") ?: [], SourceResolver::globSource($root, 'packages/*/mokowaas.xml'),
glob("{$root}/src/packages/*/*.xml") ?: [], SourceResolver::globSource($root, 'packages/*/*.xml'),
glob("{$root}/*.xml") ?: [] glob("{$root}/*.xml") ?: []
); );
foreach ($manifestFiles as $xmlFile) { foreach ($manifestFiles as $xmlFile) {
$xmlContent = file_get_contents($xmlFile); $xmlContent = file_get_contents($xmlFile);
if (strpos($xmlContent, '<extension') === false && strpos($xmlContent, '<version>') === false) { if (strpos($xmlContent, '<extension') === false && strpos($xmlContent, '<version>') === false) {
continue; continue;
} if (preg_match('#<version>(\d{2}\.\d{2}\.\d{2})((?:-(?:dev|alpha|beta|rc))+)?</version>#', $xmlContent, $xm)) { }
if (!empty($versionPrefix)) {
// Prefix-aware: look for <version>prefix + XX.YY.ZZ</version>
$prefixPattern = preg_quote($versionPrefix, '#');
if (preg_match('#<version>' . $prefixPattern . '(\d{2}\.\d{2}\.\d{2})</version>#', $xmlContent, $xm)) {
$candidate = $xm[1];
if ($manifestVersion === null || version_compare($candidate, $manifestVersion, '>')) {
$manifestVersion = $candidate;
}
continue;
}
}
if (preg_match('#<version>(\d{2}\.\d{2}\.\d{2})((?:-(?:dev|alpha|beta|rc))+)?</version>#', $xmlContent, $xm)) {
$candidate = $xm[1]; $candidate = $xm[1];
if ($manifestVersion === null || version_compare($candidate, $manifestVersion, '>')) { if ($manifestVersion === null || version_compare($candidate, $manifestVersion, '>')) {
$manifestVersion = $candidate; $manifestVersion = $candidate;
@@ -86,6 +123,28 @@ class VersionBumpCli extends CliFramework
$baseVersion = $v; $baseVersion = $v;
} }
} }
// Check --min-version: ensures dev never falls behind stable
$minVersion = $this->getArgument('--min-version');
if (!empty($minVersion)) {
$minVersion = preg_replace('/-(?:dev|alpha|beta|rc)$/', '', $minVersion);
if (preg_match('/^\d{2}\.\d{2}\.\d{2}$/', $minVersion)) {
if ($baseVersion === null || version_compare($minVersion, $baseVersion, '>')) {
$this->log('INFO', "Using --min-version {$minVersion} (higher than manifest {$baseVersion})");
$baseVersion = $minVersion;
}
}
}
// Auto-detect: scan git tags for higher versions from other channels
if ($baseVersion !== null) {
$gitTagVersion = $this->getHighestGitTagVersion($root);
if ($gitTagVersion !== null && version_compare($gitTagVersion, $baseVersion, '>')) {
$this->log('INFO', "Git tag version {$gitTagVersion} is higher than manifest {$baseVersion} — using as base");
$baseVersion = $gitTagVersion;
}
}
if ($baseVersion === null) { if ($baseVersion === null) {
$this->log('ERROR', "No version found in manifest.xml, README.md, or Joomla XML"); $this->log('ERROR', "No version found in manifest.xml, README.md, or Joomla XML");
return 1; return 1;
@@ -135,25 +194,43 @@ class VersionBumpCli extends CliFramework
} }
} }
if (file_exists($readme) && !empty($readmeContent)) { if (file_exists($readme) && !empty($readmeContent)) {
$updated = preg_replace('/(VERSION:\s*)\d{2}\.\d{2}\.\d{2}(?:(?:-(?:dev|alpha|beta|rc))+)?/m', '${1}' . $newBase, $readmeContent, 1); if (!empty($versionPrefix)) {
// Prefix-aware README replacement: preserve prefix, replace only version part
$prefixPattern = preg_quote($versionPrefix, '/');
$updated = preg_replace('/(' . $prefixPattern . ')\d{2}\.\d{2}\.\d{2}/m', '${1}' . $newBase, $readmeContent, 1);
} else {
$updated = preg_replace('/(VERSION:\s*)\d{2}\.\d{2}\.\d{2}(?:(?:-(?:dev|alpha|beta|rc))+)?/m', '${1}' . $newBase, $readmeContent, 1);
}
if ($updated !== null) { if ($updated !== null) {
file_put_contents($readme, $updated); file_put_contents($readme, $updated);
} }
} }
$updatedFiles = []; $updatedFiles = [];
foreach (["{$root}/src/pkg_*.xml", "{$root}/src/*.xml", "{$root}/src/packages/*/*.xml", "{$root}/*.xml"] as $pattern) { $srcName = SourceResolver::resolve($root);
foreach (["{$root}/{$srcName}/pkg_*.xml", "{$root}/{$srcName}/*.xml", "{$root}/{$srcName}/packages/*/*.xml", "{$root}/*.xml"] as $pattern) {
foreach (glob($pattern) ?: [] as $xmlFile) { foreach (glob($pattern) ?: [] as $xmlFile) {
$content = file_get_contents($xmlFile); $content = file_get_contents($xmlFile);
if (strpos($content, '<extension') === false) { if (strpos($content, '<extension') === false) {
continue; continue;
} }
$xmlPattern = '#<version>\d{2}\.\d{2}\.\d{2}' if (!empty($versionPrefix)) {
. '(?:(?:-(?:dev|alpha|beta|rc))+)?</version>#'; // Prefix-aware: preserve prefix, replace only the Moko version part
$newContent = preg_replace( $prefixPattern = preg_quote($versionPrefix, '#');
$xmlPattern, $xmlPattern = '#(<version>' . $prefixPattern . ')\d{2}\.\d{2}\.\d{2}</version>#';
"<version>{$newFull}</version>", $newContent = preg_replace(
$content $xmlPattern,
); '${1}' . $newBase . '</version>',
$content
);
} else {
$xmlPattern = '#<version>\d{2}\.\d{2}\.\d{2}'
. '(?:(?:-(?:dev|alpha|beta|rc))+)?</version>#';
$newContent = preg_replace(
$xmlPattern,
"<version>{$newFull}</version>",
$content
);
}
if ($newContent !== null && $newContent !== $content) { if ($newContent !== null && $newContent !== $content) {
file_put_contents($xmlFile, $newContent); file_put_contents($xmlFile, $newContent);
$updatedFiles[] = substr($xmlFile, strlen($root) + 1); $updatedFiles[] = substr($xmlFile, strlen($root) + 1);
@@ -166,28 +243,66 @@ class VersionBumpCli extends CliFramework
$packageJsonFile = "{$root}/package.json"; $packageJsonFile = "{$root}/package.json";
if (file_exists($packageJsonFile)) { if (file_exists($packageJsonFile)) {
$pkgContent = file_get_contents($packageJsonFile); $pkgContent = file_get_contents($packageJsonFile);
$pkgPattern = '/("version"\s*:\s*")\d{2}\.\d{2}\.\d{2}' if (!empty($versionPrefix)) {
. '(?:(?:-(?:dev|alpha|beta|rc))+)?(")/m'; // Prefix-aware package.json replacement
$updatedPkg = preg_replace( $prefixPattern = preg_quote($versionPrefix, '/');
$pkgPattern, $pkgPattern = '/("version"\s*:\s*")' . $prefixPattern . '\d{2}\.\d{2}\.\d{2}(")/m';
'${1}' . $newFull . '${2}', $updatedPkg = preg_replace(
$pkgContent $pkgPattern,
); '${1}' . $versionPrefix . $newBase . '${2}',
if ($updatedPkg !== $pkgContent) { $pkgContent
);
} else {
$pkgPattern = '/("version"\s*:\s*")\d{2}\.\d{2}\.\d{2}'
. '(?:(?:-(?:dev|alpha|beta|rc))+)?(")/m';
$updatedPkg = preg_replace(
$pkgPattern,
'${1}' . $newFull . '${2}',
$pkgContent
);
}
if ($updatedPkg !== $pkgContent && $updatedPkg !== null) {
file_put_contents($packageJsonFile, $updatedPkg); file_put_contents($packageJsonFile, $updatedPkg);
fwrite(STDERR, "Updated package.json\n"); fwrite(STDERR, "Updated package.json\n");
} elseif (preg_match('/("version"\s*:\s*")(\d+)\.(\d+)\.(\d+)(")/m', $pkgContent, $semM)) {
// Semver fallback: bump standard x.y.z version when XX.YY.ZZ pattern didn't match
$sMajor = (int)$semM[2];
$sMinor = (int)$semM[3];
$sPatch = (int)$semM[4];
switch ($type) {
case 'major': $sMajor++; $sMinor = 0; $sPatch = 0; break;
case 'minor': $sMinor++; $sPatch = 0; break;
default: $sPatch++; break;
}
$semNew = "{$sMajor}.{$sMinor}.{$sPatch}";
$semUpdated = preg_replace('/("version"\s*:\s*")\d+\.\d+\.\d+(")/m', '${1}' . $semNew . '${2}', $pkgContent);
if ($semUpdated !== $pkgContent) {
file_put_contents($packageJsonFile, $semUpdated);
fwrite(STDERR, "Updated package.json (semver: {$semM[2]}.{$semM[3]}.{$semM[4]} -> $semNew)\n");
}
} }
} }
$pyprojectFile = "{$root}/pyproject.toml"; $pyprojectFile = "{$root}/pyproject.toml";
if (file_exists($pyprojectFile)) { if (file_exists($pyprojectFile)) {
$pyContent = file_get_contents($pyprojectFile); $pyContent = file_get_contents($pyprojectFile);
$pyPattern = '/^(version\s*=\s*")\d{2}\.\d{2}\.\d{2}' if (!empty($versionPrefix)) {
. '(?:(?:-(?:dev|alpha|beta|rc))+)?(")/m'; // Prefix-aware pyproject.toml replacement
$updatedPy = preg_replace( $prefixPattern = preg_quote($versionPrefix, '/');
$pyPattern, $pyPattern = '/^(version\s*=\s*")' . $prefixPattern . '\d{2}\.\d{2}\.\d{2}(")/m';
'${1}' . $newFull . '${2}', $updatedPy = preg_replace(
$pyContent $pyPattern,
); '${1}' . $versionPrefix . $newBase . '${2}',
$pyContent
);
} else {
$pyPattern = '/^(version\s*=\s*")\d{2}\.\d{2}\.\d{2}'
. '(?:(?:-(?:dev|alpha|beta|rc))+)?(")/m';
$updatedPy = preg_replace(
$pyPattern,
'${1}' . $newFull . '${2}',
$pyContent
);
}
if ($updatedPy !== $pyContent) { if ($updatedPy !== $pyContent) {
file_put_contents($pyprojectFile, $updatedPy); file_put_contents($pyprojectFile, $updatedPy);
fwrite(STDERR, "Updated pyproject.toml\n"); fwrite(STDERR, "Updated pyproject.toml\n");
@@ -204,7 +319,13 @@ class VersionBumpCli extends CliFramework
} }
$scanExtensions = ['php', 'yml', 'yaml', 'md', 'txt', 'xml', 'sh', 'toml', 'ini', 'css', 'js']; $scanExtensions = ['php', 'yml', 'yaml', 'md', 'txt', 'xml', 'sh', 'toml', 'ini', 'css', 'js'];
$excludeDirs = ['.git', 'vendor', 'node_modules', 'build', 'dist', '.claude']; $excludeDirs = ['.git', 'vendor', 'node_modules', 'build', 'dist', '.claude'];
$versionPattern = '/(VERSION:\s*)\d{2}\.\d{2}\.\d{2}/m'; // Build the generic VERSION: pattern — prefix-aware if configured
if (!empty($versionPrefix)) {
$prefixPatternGeneric = preg_quote($versionPrefix, '/');
$versionPattern = '/(' . $prefixPatternGeneric . ')\d{2}\.\d{2}\.\d{2}/m';
} else {
$versionPattern = '/(VERSION:\s*)\d{2}\.\d{2}\.\d{2}/m';
}
$directory = new RecursiveDirectoryIterator($root, RecursiveDirectoryIterator::SKIP_DOTS); $directory = new RecursiveDirectoryIterator($root, RecursiveDirectoryIterator::SKIP_DOTS);
$filter = new RecursiveCallbackFilterIterator($directory, function ($current, $key, $iterator) use ($excludeDirs) { $filter = new RecursiveCallbackFilterIterator($directory, function ($current, $key, $iterator) use ($excludeDirs) {
if ($current->isDir() && in_array($current->getFilename(), $excludeDirs, true)) { if ($current->isDir() && in_array($current->getFilename(), $excludeDirs, true)) {
@@ -251,6 +372,54 @@ class VersionBumpCli extends CliFramework
echo "{$old} -> {$newFull}\n"; echo "{$old} -> {$newFull}\n";
return 0; return 0;
} }
/**
* Scan git release tags for the highest version across all channels.
*
* Checks release names like "MokoSuiteClient (VERSION: 02.45.00)" in
* git tags (stable, release-candidate, development, etc.) to find the
* highest version that has been released on any channel.
*/
private function getHighestGitTagVersion(string $root): ?string
{
$highest = null;
// Method 1: Parse version from git tag annotations / release commit messages
$output = [];
exec("cd " . escapeshellarg($root) . " && git log --all --oneline --grep='chore(version)' --grep='chore(release)' --format='%s' -20 2>/dev/null", $output);
foreach ($output as $line) {
if (preg_match('/(\d{2}\.\d{2}\.\d{2})/', $line, $m)) {
$v = preg_replace('/-(?:dev|alpha|beta|rc)$/', '', $m[1]);
if ($highest === null || version_compare($v, $highest, '>')) {
$highest = $v;
}
}
}
// Method 2: Check version in remote branches' manifest files
$branches = ['origin/main', 'origin/rc', 'origin/dev'];
$manifestPaths = ['source/pkg_*.xml', 'pkg_*.xml'];
foreach ($branches as $branch) {
foreach ($manifestPaths as $pattern) {
$files = [];
exec("cd " . escapeshellarg($root) . " && git ls-tree --name-only {$branch} -- '{$pattern}' 2>/dev/null", $files);
foreach ($files as $file) {
$content = shell_exec("cd " . escapeshellarg($root) . " && git show {$branch}:{$file} 2>/dev/null");
if ($content && preg_match('#<version>(\d{2}\.\d{2}\.\d{2})(?:-(?:dev|alpha|beta|rc))?</version>#', $content, $m)) {
$v = $m[1];
if ($highest === null || version_compare($v, $highest, '>')) {
$highest = $v;
}
}
}
}
}
return $highest;
}
} }
$app = new VersionBumpCli(); $app = new VersionBumpCli();
+17 -7
View File
@@ -6,9 +6,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: moko-platform.CLI <<<<<<< HEAD
* INGROUP: moko-platform * DEFGROUP: MokoCLI.CLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
=======
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
>>>>>>> main
* PATH: /cli/version_bump_remote.php * PATH: /cli/version_bump_remote.php
* BRIEF: Bump version in manifest XML and CHANGELOG.md on a remote branch via Gitea API * BRIEF: Bump version in manifest XML and CHANGELOG.md on a remote branch via Gitea API
*/ */
@@ -17,7 +23,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework; use MokoCli\{CliFramework, SourceResolver};
class VersionBumpRemoteCli extends CliFramework class VersionBumpRemoteCli extends CliFramework
{ {
@@ -104,11 +110,15 @@ class VersionBumpRemoteCli extends CliFramework
$nextVersion = sprintf('%02d.%02d.%02d', $major, $minor, $patch); $nextVersion = sprintf('%02d.%02d.%02d', $major, $minor, $patch);
echo "{$version} -> {$nextVersion} ({$branch})\n"; echo "{$version} -> {$nextVersion} ({$branch})\n";
// Try both source/ and src/ paths for backwards compatibility with remote repos
$manifestPaths = []; $manifestPaths = [];
if ($manifestFile !== null) { foreach (['source', 'src'] as $srcPrefix) {
$manifestPaths[] = "src/{$manifestFile}"; if ($manifestFile !== null) {
$manifestPaths[] = "{$srcPrefix}/{$manifestFile}";
}
$manifestPaths[] = "{$srcPrefix}/templateDetails.xml";
$manifestPaths[] = "{$srcPrefix}/manifest.xml";
} }
$manifestPaths = array_merge($manifestPaths, ['src/templateDetails.xml', 'src/manifest.xml']);
$manifestUpdated = false; $manifestUpdated = false;
foreach ($manifestPaths as $mPath) { foreach ($manifestPaths as $mPath) {
$result = $this->updateRemoteFile($apiBase, $token, $mPath, $branch, function (string $content) use ($version, $nextVersion): string { $result = $this->updateRemoteFile($apiBase, $token, $mPath, $branch, function (string $content) use ($version, $nextVersion): string {

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