fix: auto-download pre-built release for empty submodule sub-packages #325

Open
jmiller wants to merge 1 commits from fix/submodule-release-download into dev
+115
View File
@@ -250,6 +250,14 @@ class ReleasePackageCli extends CliFramework
}
$subZipPath = "{$outputDir}/{$subName}.zip";
// Use pre-built ZIP if staged in the output directory
if (file_exists($subZipPath) && filesize($subZipPath) > 0) {
$zip->addFile($subZipPath, "packages/{$subName}.zip");
$sizeKb = number_format(filesize($subZipPath) / 1024, 1);
echo " Sub-package: {$subName}.zip (pre-built, {$sizeKb} KB)\n";
continue;
}
// If sub-package is a full repo checkout (e.g. git submodule),
// look for a source/ or src/ subdirectory containing a Joomla manifest XML
// and zip that instead of the repo root.
@@ -270,6 +278,16 @@ class ReleasePackageCli extends CliFramework
}
}
// If source dir has no manifest, the submodule may be empty.
// Try to download the pre-built release from Gitea.
$hasManifest = !empty(glob("{$subSourceDir}/*.xml") ?: []);
if (!$hasManifest && $token !== '' && $this->downloadSubmoduleRelease($root, $subName, $subZipPath, $token)) {
$zip->addFile($subZipPath, "packages/{$subName}.zip");
$sizeKb = number_format(filesize($subZipPath) / 1024, 1);
echo " Sub-package: {$subName}.zip (downloaded release, {$sizeKb} KB)\n";
continue;
}
$subZip = new \ZipArchive();
if ($subZip->open($subZipPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) {
$this->log('ERROR', "Failed to create sub-package ZIP: {$subZipPath}");
@@ -527,6 +545,103 @@ class ReleasePackageCli extends CliFramework
return false;
}
/**
* Download a pre-built release ZIP for a sub-package that is a git submodule
* with an empty or missing source directory.
*
* Reads .gitmodules to find the submodule's remote URL, derives the Gitea
* API path, and downloads the latest stable release asset.
*/
private function downloadSubmoduleRelease(string $root, string $subName, string $destPath, string $token): bool
{
$gitmodulesPath = "{$root}/.gitmodules";
if (!file_exists($gitmodulesPath)) {
return false;
}
$gitmodules = file_get_contents($gitmodulesPath);
if ($gitmodules === false) {
return false;
}
// Find the submodule URL by matching the subName in the path
if (!preg_match('/\[submodule\s[^\]]*\]\s*\n\s*path\s*=\s*[^\n]*' . preg_quote($subName, '/') . '\s*\n\s*url\s*=\s*(\S+)/m', $gitmodules, $matches)) {
return false;
}
$remoteUrl = preg_replace('/\.git$/', '', $matches[1]);
// Extract org/repo from the URL
if (!preg_match('#[/:]([^/]+)/([^/]+)$#', $remoteUrl, $parts)) {
return false;
}
$org = $parts[1];
$repo = $parts[2];
// Derive the Gitea API base from the remote URL
$parsed = parse_url($remoteUrl);
$scheme = $parsed['scheme'] ?? 'https';
$host = $parsed['host'] ?? '';
if ($host === '') {
return false;
}
$apiBase = "{$scheme}://{$host}/api/v1/repos/{$org}/{$repo}";
echo " Submodule {$subName}: source empty, downloading release from {$org}/{$repo}...\n";
// Get the stable release
$result = $this->giteaApiRequest("{$apiBase}/releases/tags/stable", $token);
if ($result['data'] === null || !isset($result['data']['assets'])) {
echo " WARNING: No stable release found for {$org}/{$repo}\n";
return false;
}
// Find the ZIP asset (not .sha256)
$downloadUrl = '';
foreach ($result['data']['assets'] as $asset) {
if (!is_array($asset)) {
continue;
}
$name = $asset['name'] ?? '';
if (str_ends_with($name, '.zip') && !str_ends_with($name, '.sha256')) {
$downloadUrl = $asset['browser_download_url'] ?? '';
break;
}
}
if ($downloadUrl === '') {
echo " WARNING: No ZIP asset in {$org}/{$repo} stable release\n";
return false;
}
// Download the ZIP
$ch = curl_init($downloadUrl);
if ($ch === false) {
return false;
}
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTPHEADER => ["Authorization: token {$token}"],
CURLOPT_TIMEOUT => 120,
]);
$content = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode < 200 || $httpCode >= 300 || !is_string($content) || $content === '') {
echo " WARNING: Download failed (HTTP {$httpCode})\n";
return false;
}
if (file_put_contents($destPath, $content) === false) {
return false;
}
return true;
}
/**
* Recursively add files from a directory to a ZipArchive.
*/