#!/usr/bin/env php * * 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 * * Usage: * php version_reset_dev.php --token TOKEN --api-base URL * php version_reset_dev.php --token TOKEN --api-base URL --branch dev * php version_reset_dev.php --token TOKEN --api-base URL --platform dolibarr * php version_reset_dev.php --token TOKEN --api-base URL --path /repo/root * * This replaces the inline curl+python3+sed block previously used in * auto-release.yml to reset Dolibarr's $this->version on the dev branch * after a stable release. */ declare(strict_types=1); // ── Argument parsing ───────────────────────────────────────────────────────── $token = null; $apiBase = null; $branch = 'dev'; $platform = null; $path = null; foreach ($argv as $i => $arg) { if ($arg === '--token' && isset($argv[$i + 1])) { $token = $argv[$i + 1]; } if ($arg === '--api-base' && isset($argv[$i + 1])) { $apiBase = rtrim($argv[$i + 1], '/'); } if ($arg === '--branch' && isset($argv[$i + 1])) { $branch = $argv[$i + 1]; } if ($arg === '--platform' && isset($argv[$i + 1])) { $platform = $argv[$i + 1]; } if ($arg === '--path' && isset($argv[$i + 1])) { $path = $argv[$i + 1]; } if ($arg === '--help' || $arg === '-h') { printUsage(); exit(0); } } // Allow token from environment if ($token === null) { $envToken = getenv('GA_TOKEN'); if ($envToken !== false && $envToken !== '') { $token = $envToken; } } if ($token === null) { $envToken = getenv('GITEA_TOKEN'); if ($envToken !== false && $envToken !== '') { $token = $envToken; } } if ($token === null || $apiBase === null) { fwrite(STDERR, "Error: --token and --api-base are required.\n\n"); printUsage(); exit(1); } // ── Platform detection ─────────────────────────────────────────────────────── if ($platform === null && $path !== null) { $platform = detectPlatform($path); if ($platform !== null) { echo "Detected platform: {$platform}\n"; } } if ($platform === null) { fwrite(STDERR, "Error: could not determine platform. Use --platform or --path.\n"); exit(1); } // ── Dispatch by platform ───────────────────────────────────────────────────── $changed = 0; if (in_array($platform, ['dolibarr', 'crm-module'], true)) { $changed = 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"; exit(0); // ══════════════════════════════════════════════════════════════════════════════ // Helper functions // ══════════════════════════════════════════════════════════════════════════════ /** * Print usage information to stdout. * * @return void */ function printUsage(): void { echo <<<'USAGE' Reset platform version to 'development' on a branch via Gitea API. Usage: php version_reset_dev.php --token TOKEN --api-base URL [options] Required: --token TOKEN Gitea API token (also reads GA_TOKEN / GITEA_TOKEN env) --api-base URL Gitea API base URL for the repo e.g. https://git.mokoconsulting.tech/api/v1/repos/Org/Repo Options: --branch BRANCH Target branch (default: dev) --platform TYPE Platform type: dolibarr, crm-module, joomla, waas-component --path DIR Repo root for auto-detecting platform from manifest.xml --help Show this help USAGE; } /** * Detect the platform type from a repo's .mokogitea/manifest.xml file. * * @param string $repoPath Path to the repository root * @return string|null The detected platform, or null if detection fails */ 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; } /** * Make a Gitea API call and return the decoded JSON response. * * @param string $url Full API URL * @param string $token Gitea API token * @param string $method HTTP method (GET, PUT, POST, DELETE) * @param string|null $body JSON request body, or null for bodiless requests * @return array|null Decoded JSON response, or null on failure */ function giteaApiCall(string $url, string $token, string $method = 'GET', ?string $body = null): ?array { $ch = curl_init($url); if ($ch === false) { fwrite(STDERR, "Error: curl_init() failed for {$url}\n"); 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; } /** * Reset Dolibarr module version to 'development' on the target branch. * * Searches the repository tree for mod*.class.php files that contain * `extends DolibarrModules`, then replaces `$this->version = '...'` * with `$this->version = 'development'` via the Gitea file contents API. * * @param string $apiBase Gitea API base URL for the repo * @param string $token Gitea API token * @param string $branch Target branch name * @return int Number of files modified */ 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 = giteaApiCall($treeUrl, $token); if ($tree === null || !isset($tree['tree']) || !is_array($tree['tree'])) { fwrite(STDERR, "Error: could not read repository tree for branch '{$branch}'.\n"); 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 = 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 = giteaApiCall($putUrl, $token, 'PUT', $putBody); if ($result !== null) { echo "Reset: {$filePath} -> \$this->version = 'development'\n"; $changed++; } else { fwrite(STDERR, "Error: failed to update {$filePath} on branch '{$branch}'.\n"); } } return $changed; }