Files
moko-platform/cli/version_reset_dev.php
T
Jonathan Miller b3d9ee8255
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Generic: Repo Health / Release configuration (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 / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 36s
refactor(cli): migrate 64 legacy scripts to CliFramework (#235)
Wrap all CLI tools in cli/, automation/, maintenance/, deploy/, and
release/ in classes extending CliFramework. Replaces manual $argv
parsing with configure()/addArgument(), moves logic into run(): int,
and converts fwrite(STDERR,...) to $this->log(). Two CLIApp subclasses
(generate_dolibarr_version_txt, generate_joomla_update_xml) converted
to extend CliFramework directly.

Every script now gets free --help, --verbose, --quiet, --dry-run,
--json, --no-color, banners, coloured logging, and progress bars.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-31 11:39:10 -05:00

252 lines
8.5 KiB
PHP

#!/usr/bin/env php
<?php
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* FILE INFORMATION
* DEFGROUP: moko-platform.CLI
* INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/version_reset_dev.php
* BRIEF: Reset platform version to 'development' on a branch via Gitea API
*/
declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoEnterprise\CliFramework;
class VersionResetDevCli extends CliFramework
{
protected function configure(): void
{
$this->setDescription('Reset platform version to development on a branch via Gitea API');
$this->addArgument('--token', 'Gitea API token (also reads MOKOGITEA_TOKEN / GITEA_TOKEN env)', '');
$this->addArgument('--api-base', 'Gitea API base URL for the repo', '');
$this->addArgument('--branch', 'Target branch (default: dev)', 'dev');
$this->addArgument('--platform', 'Platform type: dolibarr, crm-module, joomla, waas-component', '');
$this->addArgument('--path', 'Repo root for auto-detecting platform from manifest.xml', '');
}
protected function run(): int
{
$token = $this->getArgument('--token');
$apiBase = $this->getArgument('--api-base');
$branch = $this->getArgument('--branch');
$platform = $this->getArgument('--platform');
$path = $this->getArgument('--path');
// Allow token from environment
if ($token === '') {
$envToken = getenv('MOKOGITEA_TOKEN');
if ($envToken !== false && $envToken !== '') {
$token = $envToken;
}
}
if ($token === '') {
$envToken = getenv('GITEA_TOKEN');
if ($envToken !== false && $envToken !== '') {
$token = $envToken;
}
}
if ($token === '' || $apiBase === '') {
$this->log('ERROR', '--token and --api-base are required.');
return 1;
}
$apiBase = rtrim($apiBase, '/');
// ── Platform detection ───────────────────────────────────────────────────────
if ($platform === '' && $path !== '') {
$platform = $this->detectPlatform($path) ?? '';
if ($platform !== '') {
echo "Detected platform: {$platform}\n";
}
}
if ($platform === '') {
$this->log('ERROR', 'Could not determine platform. Use --platform or --path.');
return 1;
}
// ── Dispatch by platform ─────────────────────────────────────────────────────
$changed = 0;
if (in_array($platform, ['dolibarr', 'crm-module'], true)) {
$changed = $this->resetDolibarrVersion($apiBase, $token, $branch);
} elseif (in_array($platform, ['joomla', 'waas-component'], true)) {
echo "Joomla version reset is not yet implemented — skipping.\n";
} else {
echo "Platform '{$platform}' has no version-reset logic — skipping.\n";
}
echo "Reset {$changed} file(s) to 'development' on branch '{$branch}'.\n";
return 0;
}
private function detectPlatform(string $repoPath): ?string
{
$root = realpath($repoPath) ?: $repoPath;
$manifestXml = "{$root}/.mokogitea/manifest.xml";
if (!file_exists($manifestXml)) {
return null;
}
$xml = @simplexml_load_file($manifestXml);
if ($xml === false) {
return null;
}
if (isset($xml->governance->platform)) {
$platform = (string) $xml->governance->platform;
if ($platform !== '') {
return $platform;
}
}
return null;
}
private function giteaApiCall(string $url, string $token, string $method = 'GET', ?string $body = null): ?array
{
$ch = curl_init($url);
if ($ch === false) {
$this->log('ERROR', "curl_init() failed for {$url}");
return null;
}
$headers = [
"Authorization: token {$token}",
'Accept: application/json',
];
if ($body !== null) {
$headers[] = 'Content-Type: application/json';
}
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_TIMEOUT => 30,
CURLOPT_CUSTOMREQUEST => $method,
]);
if ($body !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
}
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode < 200 || $httpCode >= 300 || !is_string($response) || $response === '') {
return null;
}
$data = json_decode($response, true);
if (!is_array($data)) {
return null;
}
return $data;
}
private function resetDolibarrVersion(string $apiBase, string $token, string $branch): int
{
// Search the repo tree for mod*.class.php files
$treeUrl = "{$apiBase}/git/trees/{$branch}?recursive=true";
$tree = $this->giteaApiCall($treeUrl, $token);
if ($tree === null || !isset($tree['tree']) || !is_array($tree['tree'])) {
$this->log('ERROR', "Could not read repository tree for branch '{$branch}'.");
return 0;
}
// Find candidate files: mod*.class.php anywhere in the tree
$candidates = [];
foreach ($tree['tree'] as $entry) {
if (!isset($entry['path']) || !is_string($entry['path'])) {
continue;
}
$basename = basename($entry['path']);
if (preg_match('/^mod[A-Za-z0-9_]+\.class\.php$/', $basename)) {
$candidates[] = $entry['path'];
}
}
if (empty($candidates)) {
echo "No mod*.class.php files found on branch '{$branch}'.\n";
return 0;
}
$changed = 0;
foreach ($candidates as $filePath) {
// GET file contents via API
$encodedPath = implode('/', array_map('rawurlencode', explode('/', $filePath)));
$fileUrl = "{$apiBase}/contents/{$encodedPath}?ref={$branch}";
$fileData = $this->giteaApiCall($fileUrl, $token);
if ($fileData === null || !isset($fileData['content'])) {
echo "Skipping {$filePath}: could not fetch contents.\n";
continue;
}
// Decode base64 content
$rawContent = is_string($fileData['content']) ? $fileData['content'] : '';
$content = base64_decode($rawContent, true);
if ($content === false) {
echo "Skipping {$filePath}: could not decode content.\n";
continue;
}
// Verify this file extends DolibarrModules
if (!str_contains($content, 'extends DolibarrModules')) {
continue;
}
// Replace $this->version = '...' with $this->version = 'development'
$updated = preg_replace(
'/(\$this->version\s*=\s*)[\'"][^\'"]*[\'"]/',
"\${1}'development'",
$content
);
if ($updated === null || $updated === $content) {
echo "Skipping {$filePath}: no version change needed.\n";
continue;
}
// PUT updated content back via API
$sha = $fileData['sha'] ?? '';
$putBody = json_encode([
'content' => base64_encode($updated),
'message' => 'chore(version): reset dev version [skip ci]',
'branch' => $branch,
'sha' => $sha,
]);
$putUrl = "{$apiBase}/contents/{$encodedPath}";
$result = $this->giteaApiCall($putUrl, $token, 'PUT', $putBody);
if ($result !== null) {
echo "Reset: {$filePath} -> \$this->version = 'development'\n";
$changed++;
} else {
$this->log('ERROR', "Failed to update {$filePath} on branch '{$branch}'.");
}
}
return $changed;
}
}
$app = new VersionResetDevCli();
exit($app->execute());