12 Commits

Author SHA1 Message Date
gitea-actions[bot] c87125ab10 chore(release): build 09.39.00 [skip ci]
Publish to Composer / Publish Package (release) Successful in 44s
2026-06-23 21:50:04 +00:00
jmiller 4e3e9b7899 Merge pull request 'feat: delete orphan workflows during sync — preserve custom-* prefix' (#315) from feature/delete-orphan-workflows into main
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 5: Template Integrity (push) Blocked by required conditions
Platform: mokocli CI / CI Summary (push) Blocked by required conditions
Platform: mokocli CI / Gate 1: Code Quality (push) Failing after 1m5s
2026-06-23 21:49:40 +00:00
gitea-actions[bot] 5a918e2d1f chore(version): auto-bump patch 09.38.02-dev [skip ci]
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Failing after 3s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 6s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 30s
2026-06-23 19:14:10 +00:00
Jonathan Miller 2d856c8846 fix: address review findings in workflow_sync.php
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) Failing after 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) Failing after 13s
Universal: PR Check / Secret Scan (pull_request) Successful in 16s
Universal: Auto Version Bump / Version Bump (push) Successful in 26s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Failing after 1m0s
- Fix #1: protect PLATFORM_EXCLUDES workflows from orphan deletion
- Fix #2: remove dead CUSTOM_DIR constant and listWorkflowEntries()
- Fix #3: fix pre-existing undefined $label bug on EXCLUDED branch
2026-06-23 14:13:40 -05:00
jmiller 200956b91a chore: sync deploy-manual.yml from Template-Generic [skip ci] 2026-06-23 18:45:41 +00:00
gitea-actions[bot] bb24a58903 chore(version): auto-bump patch 09.38.01-dev [skip ci] 2026-06-23 18:34:38 +00:00
Jonathan Miller 8cea26d748 feat: delete orphan workflows during sync — preserve custom-* prefix
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: Auto Version Bump / Version Bump (push) Successful in 29s
Universal: Build & Release / Promote to RC (pull_request) Failing after 16s
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Universal: PR Check / Secret Scan (pull_request) Successful in 6s
Universal: PR Check / Validate PR (pull_request) Failing after 4s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Failing after 1m38s
workflow_sync.php --delete-orphans now removes workflows from repos that
are not in the platform template. Protected from deletion:
- Workflows matching template names (synced normally)
- Workflows with custom- prefix (repo-specific convention)
- The custom/ subdirectory (future: subfolder discovery)
2026-06-23 13:27:52 -05:00
jmiller eb557302ce chore: remove security-audit.yml -- handled by MokoGitea
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 5: Template Integrity (push) Blocked by required conditions
Platform: mokocli CI / CI Summary (push) Blocked by required conditions
Platform: mokocli CI / Gate 1: Code Quality (push) Failing after 1m10s
2026-06-23 18:04:40 +00:00
jmiller 7e555eacf0 chore: remove deploy-manual.yml -- no longer needed
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 5: Template Integrity (push) Blocked by required conditions
Platform: mokocli CI / CI Summary (push) Blocked by required conditions
Platform: mokocli CI / Gate 1: Code Quality (push) Failing after 1m35s
2026-06-23 17:58:59 +00:00
jmiller 1d897ba115 chore: sync auto-release.yml from Template-Generic [skip ci] 2026-06-22 00:34:34 +00:00
Jonathan Miller 8e6aaaff88 feat: add synonym branch protections (master, develop, release, production, stable, staging)
Platform: mokocli CI / Gate 1: Code Quality (push) Failing after 49s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
2026-06-21 19:20:47 -05:00
Jonathan Miller fef27eb5d1 feat: add org-wide branch protection enforcement
Platform: mokocli CI / Gate 1: Code Quality (push) Failing after 1m17s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
- cli/branch_protect_org.php: enforces push whitelist on main, dev, rc,
  beta, alpha across all org repos (--dry-run supported)
- branch-protection-enforce.yml: runs weekly Monday 6am UTC + manual dispatch
- Branch flow: feature/* -> dev -> rc -> main (no direct push to any)
2026-06-21 19:08:33 -05:00
42 changed files with 388 additions and 127 deletions
+9
View File
@@ -30,6 +30,15 @@ on:
types: [opened, closed]
branches:
- main
paths-ignore:
- '.mokogitea/workflows/**'
- '*.md'
- 'wiki/**'
- '.editorconfig'
- '.gitignore'
- '.gitattributes'
- '.gitmessage'
- 'LICENSE'
workflow_dispatch:
inputs:
action:
@@ -0,0 +1,54 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# Enforce branch protection rules across all org repos.
# Runs weekly and on manual dispatch.
name: "Org: Enforce Branch Protections"
on:
schedule:
- cron: '0 6 * * 1' # Every Monday at 6am UTC
workflow_dispatch:
inputs:
dry_run:
description: 'Dry run (show changes without applying)'
required: false
type: boolean
default: false
jobs:
enforce:
name: Enforce Branch Protections
runs-on: release
steps:
- name: Checkout MokoCLI
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
token: ${{ secrets.MOKOGITEA_TOKEN }}
- name: Setup PHP
run: |
if ! command -v php > /dev/null 2>&1; then
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-curl > /dev/null 2>&1
fi
- name: Run branch protection enforcement
run: |
DRY_RUN=""
if [ "${{ inputs.dry_run }}" = "true" ]; then
DRY_RUN="--dry-run"
fi
php cli/branch_protect_org.php \
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
--org "MokoConsulting" \
$DRY_RUN
- name: Summary
if: always()
run: |
echo "## Branch Protection Enforcement" >> $GITHUB_STEP_SUMMARY
echo "All repos checked for main, dev, rc, beta, alpha protections" >> $GITHUB_STEP_SUMMARY
echo "Push whitelist: jmiller only" >> $GITHUB_STEP_SUMMARY
+1 -1
View File
@@ -5,7 +5,7 @@
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Automation
# VERSION: 01.00.00
# VERSION: 09.39.00
# BRIEF: Auto-create feature branch when an issue is opened
name: "Universal: Issue Branch"
-82
View File
@@ -1,82 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.Security
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
# PATH: /.gitea/workflows/security-audit.yml
# VERSION: 01.00.00
# BRIEF: Dependency vulnerability scanning for composer and npm packages
name: "Universal: Security Audit"
on:
schedule:
- cron: '0 6 * * 1' # Weekly on Monday at 06:00 UTC
pull_request:
branches:
- main
paths:
- 'composer.json'
- 'composer.lock'
- 'package.json'
- 'package-lock.json'
workflow_dispatch:
permissions:
contents: read
env:
NTFY_URL: ${{ vars.NTFY_URL || 'https://ntfy.mokoconsulting.tech' }}
NTFY_TOPIC: ${{ vars.NTFY_TOPIC || 'gitea-security' }}
jobs:
audit:
name: Dependency Audit
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Composer audit
if: hashFiles('composer.lock') != ''
run: |
echo "=== Composer Security Audit ==="
if ! command -v composer &> /dev/null; then
sudo apt-get update -qq
sudo apt-get install -y -qq php-cli composer >/dev/null 2>&1
fi
composer audit --format=plain 2>&1 | tee /tmp/composer-audit.txt
RESULT=$?
if [ $RESULT -ne 0 ]; then
echo "::warning::Composer vulnerabilities found"
echo "composer_vulnerable=true" >> "$GITHUB_ENV"
else
echo "No known vulnerabilities in composer dependencies"
fi
- name: NPM audit
if: hashFiles('package-lock.json') != ''
run: |
echo "=== NPM Security Audit ==="
npm audit --production 2>&1 | tee /tmp/npm-audit.txt || true
if npm audit --production 2>&1 | grep -q "found 0 vulnerabilities"; then
echo "No known vulnerabilities in npm dependencies"
else
echo "::warning::NPM vulnerabilities found"
echo "npm_vulnerable=true" >> "$GITHUB_ENV"
fi
- name: Notify on vulnerabilities
if: env.composer_vulnerable == 'true' || env.npm_vulnerable == 'true'
run: |
REPO="${{ github.event.repository.name }}"
curl -sS \
-H "Title: ${REPO} has vulnerable dependencies" \
-H "Tags: lock,warning" \
-H "Priority: high" \
-d "Security audit found vulnerabilities. Review dependency updates." \
"${NTFY_URL}/${NTFY_TOPIC}" || true
+2 -4
View File
@@ -12,7 +12,7 @@ BRIEF: Release changelog
# Changelog
## [Unreleased]
## [09.38.00] --- 2026-06-21
## [09.39.00] --- 2026-06-23
## [09.38.00] --- 2026-06-21
@@ -20,6 +20,4 @@ BRIEF: Release changelog
## [09.38.00] --- 2026-06-21
## [09.37.00] --- 2026-06-21
## [09.37.00] --- 2026-06-21
## [09.38.00] --- 2026-06-21
+1 -1
View File
@@ -6,7 +6,7 @@ DEFGROUP: MokoPlatform.Root
INGROUP: MokoPlatform
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
PATH: /README.md
VERSION: 09.38.00
VERSION: 09.39.00
BRIEF: Project overview and documentation
-->
+158
View File
@@ -0,0 +1,158 @@
<?php
/**
* @package MokoCLI
* @subpackage cli
* @author Moko Consulting <hello@mokoconsulting.tech>
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GNU General Public License version 3 or later; see LICENSE
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Enforce branch protection rules across all repos in the org.
*
* Usage:
* php cli/branch_protect_org.php --token TOKEN [--org MokoConsulting] [--dry-run]
*
* Branch flow: feature/* -> dev -> rc -> main
* main, dev, rc: push whitelist only (no direct push)
* alpha, beta: push whitelist only (pre-release)
*/
declare(strict_types=1);
$options = getopt('', ['token:', 'org:', 'api-base:', 'dry-run', 'help']);
if (isset($options['help']) || empty($options['token'])) {
echo "Usage: php cli/branch_protect_org.php --token TOKEN [--org ORG] [--api-base URL] [--dry-run]\n";
echo "\n";
echo "Options:\n";
echo " --token Gitea API token (required)\n";
echo " --org Organization name (default: MokoConsulting)\n";
echo " --api-base API base URL (default: https://git.mokoconsulting.tech/api/v1)\n";
echo " --dry-run Show what would be changed without making changes\n";
exit(0);
}
$token = $options['token'];
$org = $options['org'] ?? 'MokoConsulting';
$apiBase = rtrim($options['api-base'] ?? 'https://git.mokoconsulting.tech/api/v1', '/');
$dryRun = isset($options['dry-run']);
// Protected branches and their rules
$branchRules = [
// Primary branches (flow: feature/* -> dev -> rc -> main)
'main' => ['enable_push' => true, 'enable_push_whitelist' => true, 'push_whitelist_usernames' => ['jmiller']],
'dev' => ['enable_push' => true, 'enable_push_whitelist' => true, 'push_whitelist_usernames' => ['jmiller']],
'rc' => ['enable_push' => true, 'enable_push_whitelist' => true, 'push_whitelist_usernames' => ['jmiller']],
'beta' => ['enable_push' => true, 'enable_push_whitelist' => true, 'push_whitelist_usernames' => ['jmiller']],
'alpha' => ['enable_push' => true, 'enable_push_whitelist' => true, 'push_whitelist_usernames' => ['jmiller']],
// Synonyms (prevent bypass via alternate names)
'master' => ['enable_push' => true, 'enable_push_whitelist' => true, 'push_whitelist_usernames' => ['jmiller']],
'develop' => ['enable_push' => true, 'enable_push_whitelist' => true, 'push_whitelist_usernames' => ['jmiller']],
'release' => ['enable_push' => true, 'enable_push_whitelist' => true, 'push_whitelist_usernames' => ['jmiller']],
'production' => ['enable_push' => true, 'enable_push_whitelist' => true, 'push_whitelist_usernames' => ['jmiller']],
'stable' => ['enable_push' => true, 'enable_push_whitelist' => true, 'push_whitelist_usernames' => ['jmiller']],
'staging' => ['enable_push' => true, 'enable_push_whitelist' => true, 'push_whitelist_usernames' => ['jmiller']],
];
function apiRequest(string $method, string $url, string $token, ?array $body = null): array
{
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: token ' . $token,
'Content-Type: application/json',
'Accept: application/json',
],
CURLOPT_TIMEOUT => 30,
]);
if ($body !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
}
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return [
'status' => $httpCode,
'data' => json_decode($response, true) ?: [],
];
}
// 1. List all org repos
echo "Fetching repos for {$org}...\n";
$page = 1;
$repos = [];
do {
$result = apiRequest('GET', "{$apiBase}/orgs/{$org}/repos?limit=50&page={$page}", $token);
$batch = $result['data'];
$repos = array_merge($repos, $batch);
$page++;
} while (count($batch) === 50);
echo sprintf("Found %d repos\n\n", count($repos));
$summary = ['protected' => 0, 'added' => 0, 'skipped' => 0, 'errors' => 0];
foreach ($repos as $repo) {
$repoName = $repo['name'];
if ($repo['archived'] ?? false) {
continue;
}
// Get existing protections
$existing = apiRequest('GET', "{$apiBase}/repos/{$org}/{$repoName}/branch_protections", $token);
$existingNames = array_map(fn($p) => $p['branch_name'] ?? '', $existing['data'] ?: []);
$added = [];
$skipped = [];
foreach ($branchRules as $branch => $rules) {
if (in_array($branch, $existingNames, true)) {
$skipped[] = $branch;
$summary['skipped']++;
continue;
}
if ($dryRun) {
$added[] = $branch;
$summary['added']++;
continue;
}
$body = array_merge($rules, ['branch_name' => $branch]);
$result = apiRequest('POST', "{$apiBase}/repos/{$org}/{$repoName}/branch_protections", $token, $body);
if ($result['status'] >= 200 && $result['status'] < 300) {
$added[] = $branch;
$summary['added']++;
} elseif ($result['status'] === 422) {
$skipped[] = $branch;
$summary['skipped']++;
} else {
$added[] = "{$branch}(ERR:{$result['status']})";
$summary['errors']++;
}
}
$summary['protected']++;
if (!empty($added)) {
$prefix = $dryRun ? '[DRY-RUN] ' : '';
echo sprintf(" %s%-35s added: %s\n", $prefix, $repoName, implode(', ', $added));
}
}
echo "\n";
echo sprintf("Summary: %d repos, %d rules added, %d already existed, %d errors\n",
$summary['protected'], $summary['added'], $summary['skipped'], $summary['errors']);
if ($dryRun) {
echo "\n(Dry run - no changes made)\n";
}
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/branch_rename.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Rename a git branch via Gitea API (create new, update PR, delete old)
*/
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/bulk_workflow_push.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Push a workflow file to all governed repos via the Gitea Contents API
*/
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/bulk_workflow_trigger.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Trigger a workflow across multiple repos at once
*/
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/client_dashboard.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Generate unified client dashboard HTML
*/
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/client_inventory.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Discover and list all client-waas repos with their server configuration status
*/
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/client_provision.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Provision a new client environment end-to-end
*/
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/grafana_dashboard.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Manage Grafana dashboards via API
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/joomla_build.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Build a Joomla extension ZIP from manifest — all types supported
* NOTE: Called by pre-release and auto-release workflows.
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/joomla_metadata_validate.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Validate MokoGitea repo metadata against Joomla extension manifest XML
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/manifest_detect.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Auto-detect manifest fields from source files and optionally push to API
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/manifest_integrity.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Cross-check manifest API fields against repo contents across the org
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/manifest_licensing.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Ensure licensing tags (updateservers, dlid) in Joomla extension manifests
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/manifest_read.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Read repo metadata from Gitea manifest API, auto-detect the rest
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/platform_detect.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Auto-detect repository platform type and optionally update manifest
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/release_cascade.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Cascade release zip to all lower stability channels
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/release_publish.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Publish a release and create copies for all lesser stability streams.
*/
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/scaffold_client.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Scaffold a new client-waas repo from Template-Client-WaaS with pre-configured settings
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/updates_xml_sync.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Sync updates.xml to target branches via Gitea API
* NOTE: Called by pre-release and auto-release workflows after updates.xml
* is modified on the current branch. Pushes the file to other branches
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/version_auto_bump.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Auto patch-bump, set stability suffix, and commit — single CLI replacing inline workflow bash
*/
+1 -1
View File
@@ -370,7 +370,7 @@ class VersionBumpCli extends CliFramework
/**
* Scan git release tags for the highest version across all channels.
*
* Checks release names like "MokoSuiteClient (VERSION: 09.38.00)" in
* Checks release names like "MokoSuiteClient (VERSION: 09.39.00)" in
* git tags (stable, release-candidate, development, etc.) to find the
* highest version that has been released on any channel.
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/version_check.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Validate version consistency across README, manifests, and sub-packages
*/
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/wiki_sync.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Sync select wiki pages from mokocli to all template repos
*/
+128 -4
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/workflow_sync.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Sync workflows from Generic platform templates live repos based on manifest.platform
*/
@@ -42,9 +42,13 @@ class WorkflowSyncCli extends CliFramework
'joomla' => ['deploy-manual.yml'],
];
/** Prefix for custom workflows preserved during orphan cleanup. */
private const CUSTOM_PREFIX = 'custom-';
private int $updated = 0;
private int $created = 0;
private int $skipped = 0;
private int $deleted = 0;
private int $errors = 0;
protected function configure(): void
@@ -56,6 +60,7 @@ class WorkflowSyncCli extends CliFramework
$this->addArgument('--branch', 'Target branch (default: main)', 'main');
$this->addArgument('--phase', 'Phase to run: all, templates, repos (default: all)', 'all');
$this->addArgument('--platform-filter', 'Only sync repos matching this platform', '');
$this->addArgument('--delete-orphans', 'Delete workflows not in template (preserves custom-* and custom/)', false);
}
protected function run(): int
@@ -114,7 +119,7 @@ class WorkflowSyncCli extends CliFramework
echo "\n";
$this->log('INFO', "Done: {$this->created} created, {$this->updated} updated, "
. "{$this->skipped} skipped, {$this->errors} error(s).");
. "{$this->deleted} deleted, {$this->skipped} skipped, {$this->errors} error(s).");
return $this->errors > 0 ? 1 : 0;
}
@@ -275,14 +280,15 @@ class WorkflowSyncCli extends CliFramework
foreach ($workflows as $workflow) {
$filename = $workflow['name'];
$destPath = '.mokogitea/workflows/' . $filename;
$label = "{$repoFullName}/{$filename}";
// Skip platform-excluded workflows
if (in_array($filename, self::PLATFORM_EXCLUDES[$platform] ?? [], true)) {
fprintf(STDERR, "%-45s | %s\n", $label, 'EXCLUDED (platform)');
$this->skipped++;
continue;
}
$destPath = '.mokogitea/workflows/' . $filename;
$label = "{$repoFullName}/{$filename}";
// Get source content from template
$sourceContent = $this->getFileContent(
@@ -303,6 +309,14 @@ class WorkflowSyncCli extends CliFramework
$destPath, $sourceContent, $branch, $commitMsg, $label
);
}
// Delete orphan workflows if enabled
if ($this->getArgument('--delete-orphans', false)) {
$templateNames = array_map(fn($w) => $w['name'], $workflows);
$this->deleteOrphanWorkflows(
$giteaUrl, $token, $org, $repoName, $branch, $templateNames, $platform
);
}
}
echo "\n";
@@ -406,6 +420,116 @@ class WorkflowSyncCli extends CliFramework
}
}
/**
* Delete workflows in a repo that are NOT in the template and NOT custom.
*
* Protected from deletion:
* - Files matching template workflow names
* - Files with `custom-` prefix (convention for repo-specific workflows)
* - Directories named `custom` (future: subfolder discovery)
* - Platform-excluded workflows
*/
private function deleteOrphanWorkflows(
string $giteaUrl,
string $token,
string $org,
string $repoName,
string $branch,
array $templateNames,
string $platform
): void {
$repoWorkflows = $this->listWorkflows($giteaUrl, $token, $org, $repoName, $branch);
if ($repoWorkflows === null) {
return;
}
$platformExcludes = self::PLATFORM_EXCLUDES[$platform] ?? [];
foreach ($repoWorkflows as $workflow) {
$name = $workflow['name'];
// Keep if it's in the template
if (in_array($name, $templateNames, true)) {
continue;
}
// Keep if it has the custom- prefix
if (str_starts_with($name, self::CUSTOM_PREFIX)) {
$label = "{$org}/{$repoName}/{$name}";
fprintf(STDERR, "%-45s | %s\n", $label, 'KEPT (custom)');
continue;
}
// Keep if it's platform-excluded (legitimately skipped during sync)
if (in_array($name, $platformExcludes, true)) {
$label = "{$org}/{$repoName}/{$name}";
fprintf(STDERR, "%-45s | %s\n", $label, 'KEPT (platform-excluded)');
continue;
}
// Delete orphan
$filePath = '.mokogitea/workflows/' . $name;
$label = "{$org}/{$repoName}/{$name}";
if ($this->dryRun) {
fprintf(STDERR, "%-45s | %s\n", $label, 'WOULD DELETE');
$this->deleted++;
continue;
}
$deleted = $this->deleteFile($giteaUrl, $token, $org, $repoName, $filePath, $branch);
if ($deleted) {
fprintf(STDERR, "%-45s | %s\n", $label, 'DELETED');
$this->deleted++;
} else {
fprintf(STDERR, "%-45s | %s\n", $label, 'ERROR (delete)');
$this->errors++;
}
}
}
/**
* Delete a file from a repo via the Gitea Contents API.
*/
private function deleteFile(
string $giteaUrl,
string $token,
string $org,
string $repoName,
string $filePath,
string $branch
): bool {
// Get SHA first
$existing = $this->apiRequest(
$giteaUrl, $token, 'GET',
"/api/v1/repos/{$org}/{$repoName}/contents/{$filePath}?ref={$branch}"
);
if ($existing['code'] !== 200) {
return false;
}
$data = json_decode($existing['body'], true);
$sha = $data['sha'] ?? '';
if ($sha === '') {
return false;
}
$payload = json_encode([
'sha' => $sha,
'message' => "chore: delete orphan workflow {$filePath} [skip ci]",
'branch' => $branch,
]);
$response = $this->apiRequest(
$giteaUrl, $token, 'DELETE',
"/api/v1/repos/{$org}/{$repoName}/contents/{$filePath}",
$payload
);
return $response['code'] === 200;
}
/**
* List workflow files in a repo's .mokogitea/workflows/ directory.
*/
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /deploy/backup-before-deploy.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Snapshot Joomla directories before deployment for rollback capability
*/
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /deploy/deploy-dolibarr.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Deploy Dolibarr module files to a remote server via SFTP/rsync
*/
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /deploy/health-check.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Post-deploy health check verify a Joomla site is responding correctly
*/
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /deploy/rollback-joomla.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Rollback a Joomla deployment by restoring from a pre-deploy snapshot
*/
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /deploy/sync-joomla.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Sync Joomla site directories between two servers via rsync over SSH
*/
+1 -1
View File
@@ -14,7 +14,7 @@
DEFGROUP: dolibarr-api-mcp.Documentation
INGROUP: dolibarr-api-mcp
REPO: https://git.mokoconsulting.tech/MokoConsulting/dolibarr-api-mcp
VERSION: 09.38.00
VERSION: 09.39.00
PATH: ./CONTRIBUTING.md
BRIEF: Contribution guidelines for the project
-->
+1 -1
View File
@@ -10,7 +10,7 @@ DEFGROUP: dolibarr-api-mcp.Documentation
INGROUP: dolibarr-api-mcp
REPO: https://git.mokoconsulting.tech/MokoConsulting/dolibarr-api-mcp
PATH: /SECURITY.md
VERSION: 09.38.00
VERSION: 09.39.00
BRIEF: Security vulnerability reporting and handling policy
-->
+1 -1
View File
@@ -14,7 +14,7 @@
DEFGROUP:
INGROUP: Project.Documentation
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoCli-Template-Generic
VERSION: 09.38.00
VERSION: 09.39.00
PATH: ./CONTRIBUTING.md
BRIEF: Contribution guidelines for the project
-->
+1 -1
View File
@@ -23,7 +23,7 @@ DEFGROUP: [PROJECT_NAME]
INGROUP: [PROJECT_NAME].Documentation
REPO: [REPOSITORY_URL]
PATH: /SECURITY.md
VERSION: 09.38.00
VERSION: 09.39.00
BRIEF: Security vulnerability reporting and handling policy
-->
+1 -1
View File
@@ -63,7 +63,7 @@ class VersionBumpTest extends TestCase
{
file_put_contents(
"{$this->tmpDir}/README.md",
"<!-- VERSION: 09.38.00 -->\nSome content\n"
"<!-- VERSION: 09.39.00 -->\nSome content\n"
);
$this->execute();
+2 -2
View File
@@ -34,7 +34,7 @@ class VersionReadTest extends TestCase
{
file_put_contents(
"{$this->tmpDir}/README.md",
"# Test\n<!-- VERSION: 09.38.00 -->\n"
"# Test\n<!-- VERSION: 09.39.00 -->\n"
);
$this->assertSame('02.03.04', trim($this->runScript()));
@@ -68,7 +68,7 @@ class VersionReadTest extends TestCase
{
file_put_contents(
"{$this->tmpDir}/README.md",
"<!-- VERSION: 09.38.00 -->\n"
"<!-- VERSION: 09.39.00 -->\n"
);
mkdir("{$this->tmpDir}/src", 0755, true);
file_put_contents(
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /validate/check_file_integrity.php
* VERSION: 09.38.00
* VERSION: 09.39.00
* BRIEF: Compare deployed files on a remote server against the local repository to detect drift
*/