#!/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_bump.php * BRIEF: Auto-increment version — manifest.xml is canonical, cascades to all XML and MD files */ declare(strict_types=1); $path = '.'; $type = 'patch'; // patch | minor | major foreach ($argv as $i => $arg) { if ($arg === '--path' && isset($argv[$i + 1])) $path = $argv[$i + 1]; if ($arg === '--minor') $type = 'minor'; if ($arg === '--major') $type = 'major'; } $root = realpath($path) ?: $path; // -- 1. Read version from .mokogitea/manifest.xml (canonical) -- $mokoVersion = null; $mokoManifest = "{$root}/.mokogitea/manifest.xml"; $mokoContent = ''; if (file_exists($mokoManifest)) { $mokoContent = file_get_contents($mokoManifest); if (preg_match('|(\d{2}\.\d{2}\.\d{2})(?:-([a-z]+))?|', $mokoContent, $m)) { $mokoVersion = $m[1]; } } // -- 2. Fallback: README.md -- $readmeVersion = null; $readme = "{$root}/README.md"; $readmeContent = ''; if (file_exists($readme)) { $readmeContent = file_get_contents($readme); if (preg_match('/VERSION:\s*(\d{2}\.\d{2}\.\d{2})/m', $readmeContent, $m)) { $readmeVersion = $m[1]; } } // -- 3. Fallback: Joomla manifest XML -- $manifestVersion = null; $manifestFiles = array_merge( glob("{$root}/src/pkg_*.xml") ?: [], glob("{$root}/src/*.xml") ?: [], glob("{$root}/src/packages/*/mokowaas.xml") ?: [], glob("{$root}/src/packages/*/*.xml") ?: [], glob("{$root}/*.xml") ?: [] ); $manifestSuffix = ''; foreach ($manifestFiles as $xmlFile) { $xmlContent = file_get_contents($xmlFile); if (strpos($xmlContent, '') === false) { continue; } if (preg_match('|(\d{2}\.\d{2}\.\d{2})(?:-([a-z]+))?|', $xmlContent, $xm)) { $candidate = $xm[1]; if ($manifestVersion === null || version_compare($candidate, $manifestVersion, '>')) { $manifestVersion = $candidate; $manifestSuffix = isset($xm[2]) ? $xm[2] : ''; } } } // -- Use the highest version as base -- $baseVersion = null; $candidates = array_filter([$mokoVersion, $readmeVersion, $manifestVersion]); foreach ($candidates as $v) { if ($baseVersion === null || version_compare($v, $baseVersion, '>')) { $baseVersion = $v; } } if ($baseVersion === null) { fwrite(STDERR, "No version found in manifest.xml, README.md, or Joomla XML\n"); exit(1); } // -- Parse and bump -- if (!preg_match('/^(\d{2})\.(\d{2})\.(\d{2})$/', $baseVersion, $parts)) { fwrite(STDERR, "Invalid version format: {$baseVersion}\n"); exit(1); } $major = (int)$parts[1]; $minor = (int)$parts[2]; $patch = (int)$parts[3]; $old = sprintf('%02d.%02d.%02d', $major, $minor, $patch); switch ($type) { case 'major': $major++; $minor = 0; $patch = 0; break; case 'minor': $minor++; $patch = 0; break; default: $patch++; if ($patch > 99) { $minor++; $patch = 0; } if ($minor > 99) { $major++; $minor = 0; } break; } $new = sprintf('%02d.%02d.%02d', $major, $minor, $patch); // -- Determine suffix to preserve (from whichever source had the version) -- $suffix = !empty($manifestSuffix) ? $manifestSuffix : ''; $newFull = $suffix !== '' ? "{$new}-{$suffix}" : $new; // -- Update .mokogitea/manifest.xml (canonical target, no suffix) -- if (file_exists($mokoManifest) && !empty($mokoContent)) { $updated = preg_replace( '|\d{2}\.\d{2}\.\d{2}(?:-[a-z]+)?|', "{$new}", $mokoContent, 1 ); file_put_contents($mokoManifest, $updated); } // -- Update README.md -- if (file_exists($readme) && !empty($readmeContent)) { $updated = preg_replace( '/(VERSION:\s*)\d{2}\.\d{2}\.\d{2}/m', '${1}' . $new, $readmeContent, 1 ); file_put_contents($readme, $updated); } // -- Cascade to ALL Joomla extension XML manifests -- $xmlPatterns = [ "{$root}/src/pkg_*.xml", "{$root}/src/*.xml", "{$root}/src/packages/*/*.xml", "{$root}/*.xml", ]; $updatedFiles = []; foreach ($xmlPatterns as $pattern) { foreach (glob($pattern) ?: [] as $xmlFile) { $content = file_get_contents($xmlFile); // Only update files that have an tag (Joomla manifests) if (strpos($content, '\d{2}\.\d{2}\.\d{2}(?:-[a-z]+)?|', "{$newFull}", $content ); if ($newContent !== $content) { file_put_contents($xmlFile, $newContent); $updatedFiles[] = substr($xmlFile, strlen($root) + 1); } } } if (!empty($updatedFiles)) { fwrite(STDERR, "Updated " . count($updatedFiles) . " Joomla manifest(s): " . implode(', ', $updatedFiles) . "\n"); } // -- Update package.json (Node.js / MCP) -- $packageJsonFile = "{$root}/package.json"; if (file_exists($packageJsonFile)) { $pkgContent = file_get_contents($packageJsonFile); $updatedPkg = preg_replace( '/("version"\s*:\s*")\d{2}\.\d{2}\.\d{2}(")/m', '${1}' . $new . '${2}', $pkgContent ); if ($updatedPkg !== $pkgContent) { file_put_contents($packageJsonFile, $updatedPkg); fwrite(STDERR, "Updated package.json\n"); } } // -- Update pyproject.toml (Python) -- $pyprojectFile = "{$root}/pyproject.toml"; if (file_exists($pyprojectFile)) { $pyContent = file_get_contents($pyprojectFile); $updatedPy = preg_replace( '/^(version\s*=\s*")\d{2}\.\d{2}\.\d{2}(")/m', '${1}' . $new . '${2}', $pyContent ); if ($updatedPy !== $pyContent) { file_put_contents($pyprojectFile, $updatedPy); fwrite(STDERR, "Updated pyproject.toml\n"); } } echo "{$old} -> {$newFull}\n"; exit(0);