Merge pull request 'fix: package_build.php Joomla package builds + PHPStan level 2' (#95) from dev into main
Universal: Cascade Main → Dev / Cascade main → branches (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Generic: Repo Health / Site Health (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
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (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 / CI Summary (push) Has been cancelled

This commit was merged in pull request #95.
This commit is contained in:
2026-05-26 00:39:24 +00:00
2 changed files with 226 additions and 163 deletions
+1
View File
@@ -26,6 +26,7 @@ Version format: `XX.YY.ZZ` (zero-padded semver).
### Fixed ### Fixed
- `release_cascade.php`: accept `release-candidate` as stability value (was only accepting `rc`, causing cascade to silently skip) - `release_cascade.php`: accept `release-candidate` as stability value (was only accepting `rc`, causing cascade to silently skip)
- PHPStan bumped from level 0 to level 2 — fixed 67 type errors (undefined variables, missing methods, wrong signatures, dead code) - PHPStan bumped from level 0 to level 2 — fixed 67 type errors (undefined variables, missing methods, wrong signatures, dead code)
- `package_build.php`: fix 0-byte ZIP for Joomla package extensions — sub-zips now in `packages/` subdir, no double `pkg_pkg_` prefix, includes `language/` dir (closes #92)
## [06.00.00] - 2026-05-25 ## [06.00.00] - 2026-05-25
+87 -25
View File
@@ -1,5 +1,6 @@
#!/usr/bin/env php #!/usr/bin/env php
<?php <?php
/* 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
@@ -37,12 +38,24 @@ $elementOverride = null;
$githubOutput = false; $githubOutput = false;
foreach ($argv as $i => $arg) { foreach ($argv as $i => $arg) {
if ($arg === '--path' && isset($argv[$i + 1])) $path = $argv[$i + 1]; if ($arg === '--path' && isset($argv[$i + 1])) {
if ($arg === '--version' && isset($argv[$i + 1])) $version = $argv[$i + 1]; $path = $argv[$i + 1];
if ($arg === '--output-dir' && isset($argv[$i + 1])) $outputDir = $argv[$i + 1]; }
if ($arg === '--type-prefix' && isset($argv[$i + 1])) $typePrefixOverride = $argv[$i + 1]; if ($arg === '--version' && isset($argv[$i + 1])) {
if ($arg === '--element' && isset($argv[$i + 1])) $elementOverride = $argv[$i + 1]; $version = $argv[$i + 1];
if ($arg === '--github-output') $githubOutput = true; }
if ($arg === '--output-dir' && isset($argv[$i + 1])) {
$outputDir = $argv[$i + 1];
}
if ($arg === '--type-prefix' && isset($argv[$i + 1])) {
$typePrefixOverride = $argv[$i + 1];
}
if ($arg === '--element' && isset($argv[$i + 1])) {
$elementOverride = $argv[$i + 1];
}
if ($arg === '--github-output') {
$githubOutput = true;
}
} }
if ($version === null) { if ($version === null) {
@@ -94,24 +107,45 @@ if ($extElement === null || $typePrefixOverride === null) {
$xml = file_get_contents($manifest); $xml = file_get_contents($manifest);
if ($extElement === null) { if ($extElement === null) {
if (preg_match('/<element>([^<]+)<\/element>/', $xml, $m)) $extElement = $m[1]; if (preg_match('/<element>([^<]+)<\/element>/', $xml, $m)) {
elseif (preg_match('/plugin="([^"]+)"/', $xml, $m)) $extElement = $m[1]; $extElement = $m[1];
elseif (preg_match('/module="([^"]+)"/', $xml, $m)) $extElement = $m[1]; } elseif (preg_match('/plugin="([^"]+)"/', $xml, $m)) {
else $extElement = strtolower(pathinfo($manifest, PATHINFO_FILENAME)); $extElement = $m[1];
} elseif (preg_match('/module="([^"]+)"/', $xml, $m)) {
$extElement = $m[1];
} else {
$extElement = strtolower(pathinfo($manifest, PATHINFO_FILENAME));
}
} }
if (preg_match('/<extension[^>]*type="([^"]+)"/', $xml, $m)) $extType = $m[1]; if (preg_match('/<extension[^>]*type="([^"]+)"/', $xml, $m)) {
$extType = $m[1];
}
$extFolder = ''; $extFolder = '';
if (preg_match('/<extension[^>]*group="([^"]+)"/', $xml, $m)) $extFolder = $m[1]; if (preg_match('/<extension[^>]*group="([^"]+)"/', $xml, $m)) {
$extFolder = $m[1];
}
if ($typePrefixOverride === null) { if ($typePrefixOverride === null) {
switch ($extType) { switch ($extType) {
case 'plugin': $typePrefix = "plg_{$extFolder}_"; break; case 'plugin':
case 'module': $typePrefix = 'mod_'; break; $typePrefix = "plg_{$extFolder}_";
case 'component': $typePrefix = 'com_'; break; break;
case 'template': $typePrefix = 'tpl_'; break; case 'module':
case 'library': $typePrefix = 'lib_'; break; $typePrefix = 'mod_';
case 'package': $typePrefix = 'pkg_'; break; break;
case 'component':
$typePrefix = 'com_';
break;
case 'template':
$typePrefix = 'tpl_';
break;
case 'library':
$typePrefix = 'lib_';
break;
case 'package':
$typePrefix = 'pkg_';
break;
} }
} }
@@ -123,6 +157,11 @@ if ($extElement === null) {
$extElement = strtolower(basename($root)); $extElement = strtolower(basename($root));
} }
// Prevent double prefix (e.g. pkg_pkg_mokogallery)
if ($typePrefix !== '' && str_starts_with($extElement, rtrim($typePrefix, '_'))) {
$extElement = substr($extElement, strlen(rtrim($typePrefix, '_')) + 1);
}
$zipName = "{$typePrefix}{$extElement}-{$version}.zip"; $zipName = "{$typePrefix}{$extElement}-{$version}.zip";
$tarName = "{$typePrefix}{$extElement}-{$version}.tar.gz"; $tarName = "{$typePrefix}{$extElement}-{$version}.tar.gz";
$zipPath = "{$outputDir}/{$zipName}"; $zipPath = "{$outputDir}/{$zipName}";
@@ -143,15 +182,16 @@ if ($isPackage) {
echo "=== Building Joomla PACKAGE (multi-extension) ===\n"; echo "=== Building Joomla PACKAGE (multi-extension) ===\n";
$stagingDir = sys_get_temp_dir() . '/moko-pkg-' . uniqid(); $stagingDir = sys_get_temp_dir() . '/moko-pkg-' . uniqid();
mkdir($stagingDir, 0755, true); $packagesDir = "{$stagingDir}/packages";
mkdir($packagesDir, 0755, true);
// ZIP each sub-extension // ZIP each sub-extension into packages/
foreach (glob("{$sourceDir}/packages/*/") ?: [] as $extDir) { foreach (glob("{$sourceDir}/packages/*/") ?: [] as $extDir) {
$subName = basename($extDir); $subName = basename($extDir);
echo " Packaging sub-extension: {$subName}\n"; echo " Packaging sub-extension: {$subName}\n";
$subZip = new ZipArchive(); $subZip = new ZipArchive();
$subZipPath = "{$stagingDir}/{$subName}.zip"; $subZipPath = "{$packagesDir}/{$subName}.zip";
if ($subZip->open($subZipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) { if ($subZip->open($subZipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
fwrite(STDERR, "Failed to create ZIP for {$subName}\n"); fwrite(STDERR, "Failed to create ZIP for {$subName}\n");
continue; continue;
@@ -159,13 +199,32 @@ if ($isPackage) {
addDirectoryToZip($subZip, $extDir, '', $excludePatterns); addDirectoryToZip($subZip, $extDir, '', $excludePatterns);
$subZip->close(); $subZip->close();
echo " -> packages/{$subName}.zip (" . filesize($subZipPath) . " bytes)\n";
} }
// Copy package-level files // Copy package-level files (manifest, script.php, etc.)
foreach (array_merge(glob("{$sourceDir}/*.xml") ?: [], glob("{$sourceDir}/*.php") ?: []) as $f) { foreach (array_merge(glob("{$sourceDir}/*.xml") ?: [], glob("{$sourceDir}/*.php") ?: []) as $f) {
copy($f, "{$stagingDir}/" . basename($f)); copy($f, "{$stagingDir}/" . basename($f));
} }
// Copy language directory if present
if (is_dir("{$sourceDir}/language")) {
$langDest = "{$stagingDir}/language";
mkdir($langDest, 0755, true);
$langIterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator("{$sourceDir}/language", RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($langIterator as $item) {
$target = $langDest . '/' . substr($item->getPathname(), strlen("{$sourceDir}/language") + 1);
if ($item->isDir()) {
mkdir($target, 0755, true);
} else {
copy($item->getPathname(), $target);
}
}
}
// Create ZIP from staging // Create ZIP from staging
$zip = new ZipArchive(); $zip = new ZipArchive();
if ($zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) { if ($zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
@@ -186,7 +245,6 @@ if ($isPackage) {
// Cleanup staging // Cleanup staging
$cleanCmd = sprintf('rm -rf %s', escapeshellarg($stagingDir)); $cleanCmd = sprintf('rm -rf %s', escapeshellarg($stagingDir));
passthru($cleanCmd); passthru($cleanCmd);
} else { } else {
echo "=== Building standard extension package ===\n"; echo "=== Building standard extension package ===\n";
@@ -245,7 +303,9 @@ if ($githubOutput) {
file_put_contents($ghOutput, implode("\n", $lines) . "\n", FILE_APPEND); file_put_contents($ghOutput, implode("\n", $lines) . "\n", FILE_APPEND);
fwrite(STDERR, "Exported " . count($lines) . " fields to GITHUB_OUTPUT\n"); fwrite(STDERR, "Exported " . count($lines) . " fields to GITHUB_OUTPUT\n");
} else { } else {
foreach ($lines as $line) echo "{$line}\n"; foreach ($lines as $line) {
echo "{$line}\n";
}
} }
} }
@@ -274,7 +334,9 @@ function addDirectoryToZip(ZipArchive $zip, string $dir, string $prefix, array $
break; break;
} }
} }
if ($skip) continue; if ($skip) {
continue;
}
// Normalize path separators for ZIP // Normalize path separators for ZIP
$relativePath = str_replace('\\', '/', $relativePath); $relativePath = str_replace('\\', '/', $relativePath);