Bridge: fetch stable URL from MokoOnyx updates.xml, fix double-exec
Some checks failed
Repo Health / Access control (push) Successful in 1s
Auto-Update SHA Hash / Update SHA-256 Hash in updates.xml (release) Failing after 5s
Repo Health / Release configuration (push) Failing after 3s
Repo Health / Scripts governance (push) Successful in 3s
Repo Health / Repository health (push) Failing after 3s
Some checks failed
Repo Health / Access control (push) Successful in 1s
Auto-Update SHA Hash / Update SHA-256 Hash in updates.xml (release) Failing after 5s
Repo Health / Release configuration (push) Failing after 3s
Repo Health / Scripts governance (push) Successful in 3s
Repo Health / Repository health (push) Failing after 3s
- Remove hardcoded RELEASE_URL, discover from updates.xml stable channel - Add discoverStableUrl() to parse MokoOnyx updates.xml at runtime - Extract httpGet() helper for reuse across download + XML fetch - Remove bridge call from update() — postflight() handles it - Always targets stable channel for production-safe installs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -28,8 +28,11 @@ class MokoBridgeMigration
|
|||||||
private const OLD_DISPLAY = 'MokoCassiopeia';
|
private const OLD_DISPLAY = 'MokoCassiopeia';
|
||||||
private const NEW_DISPLAY = 'MokoOnyx';
|
private const NEW_DISPLAY = 'MokoOnyx';
|
||||||
|
|
||||||
/** URL to the latest MokoOnyx stable release ZIP */
|
/** Raw URL for MokoOnyx updates.xml on main — used to discover the stable download URL */
|
||||||
private const RELEASE_URL = 'https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/v01/mokoonyx-01.00.00.zip';
|
private const UPDATES_XML_URL = 'https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/updates.xml';
|
||||||
|
|
||||||
|
/** Fallback URL if updates.xml cannot be parsed */
|
||||||
|
private const FALLBACK_URL = 'https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/v01/mokoonyx-01.00.00.zip';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run the full migration.
|
* Run the full migration.
|
||||||
@@ -85,15 +88,94 @@ class MokoBridgeMigration
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Download the MokoOnyx ZIP to Joomla's tmp directory.
|
* Download the MokoOnyx ZIP to Joomla's tmp directory.
|
||||||
|
*
|
||||||
|
* Reads MokoOnyx's updates.xml on main to discover the current stable
|
||||||
|
* download URL, falling back to a hardcoded URL if parsing fails.
|
||||||
*/
|
*/
|
||||||
private static function downloadRelease(): ?string
|
private static function downloadRelease(): ?string
|
||||||
{
|
{
|
||||||
$tmpDir = Factory::getApplication()->get('tmp_path', JPATH_ROOT . '/tmp');
|
$tmpDir = Factory::getApplication()->get('tmp_path', JPATH_ROOT . '/tmp');
|
||||||
$zipPath = $tmpDir . '/mokoonyx-install.zip';
|
$zipPath = $tmpDir . '/mokoonyx-install.zip';
|
||||||
|
|
||||||
|
// 1. Discover the stable download URL from MokoOnyx's updates.xml
|
||||||
|
$releaseUrl = self::discoverStableUrl();
|
||||||
|
if (!$releaseUrl) {
|
||||||
|
self::log('Bridge: could not discover release URL from updates.xml, using fallback');
|
||||||
|
$releaseUrl = self::FALLBACK_URL;
|
||||||
|
}
|
||||||
|
|
||||||
|
self::log('Bridge: downloading MokoOnyx from ' . $releaseUrl);
|
||||||
|
|
||||||
|
// 2. Download the ZIP
|
||||||
|
$content = self::httpGet($releaseUrl);
|
||||||
|
|
||||||
|
if ($content === false || strlen($content) < 1000) {
|
||||||
|
self::log('Bridge: failed to download MokoOnyx ZIP from ' . $releaseUrl, 'error');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_put_contents($zipPath, $content) === false) {
|
||||||
|
self::log('Bridge: failed to write ZIP to ' . $zipPath, 'error');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
self::log('Bridge: downloaded MokoOnyx ZIP (' . strlen($content) . ' bytes)');
|
||||||
|
return $zipPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch MokoOnyx's updates.xml and extract the stable channel ZIP URL.
|
||||||
|
*
|
||||||
|
* Always targets the stable channel — the bridge should only install
|
||||||
|
* production-ready builds of MokoOnyx.
|
||||||
|
*/
|
||||||
|
private static function discoverStableUrl(): ?string
|
||||||
|
{
|
||||||
|
$xml = self::httpGet(self::UPDATES_XML_URL);
|
||||||
|
if ($xml === false || strlen($xml) < 100) {
|
||||||
|
self::log('Bridge: failed to fetch MokoOnyx updates.xml', 'warning');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
libxml_use_internal_errors(true);
|
||||||
|
$doc = simplexml_load_string($xml);
|
||||||
|
libxml_clear_errors();
|
||||||
|
|
||||||
|
if (!$doc) {
|
||||||
|
self::log('Bridge: failed to parse MokoOnyx updates.xml', 'warning');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the stable <update> block
|
||||||
|
foreach ($doc->update as $update) {
|
||||||
|
$tags = $update->tags->tag ?? [];
|
||||||
|
foreach ($tags as $tag) {
|
||||||
|
if ((string) $tag === 'stable') {
|
||||||
|
foreach ($update->downloads->downloadurl as $dl) {
|
||||||
|
$format = (string) ($dl['format'] ?? '');
|
||||||
|
$url = trim((string) $dl);
|
||||||
|
if ($format === 'zip' && !empty($url)) {
|
||||||
|
self::log('Bridge: discovered stable URL: ' . $url);
|
||||||
|
return $url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self::log('Bridge: no stable ZIP URL found in MokoOnyx updates.xml', 'warning');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP GET helper — tries file_get_contents then cURL.
|
||||||
|
*
|
||||||
|
* @return string|false Response body or false on failure.
|
||||||
|
*/
|
||||||
|
private static function httpGet(string $url)
|
||||||
|
{
|
||||||
$content = false;
|
$content = false;
|
||||||
|
|
||||||
// Method 1: file_get_contents
|
|
||||||
if (ini_get('allow_url_fopen')) {
|
if (ini_get('allow_url_fopen')) {
|
||||||
$ctx = stream_context_create([
|
$ctx = stream_context_create([
|
||||||
'http' => [
|
'http' => [
|
||||||
@@ -106,12 +188,11 @@ class MokoBridgeMigration
|
|||||||
'verify_peer_name' => true,
|
'verify_peer_name' => true,
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
$content = @file_get_contents(self::RELEASE_URL, false, $ctx);
|
$content = @file_get_contents($url, false, $ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method 2: cURL
|
|
||||||
if ($content === false && function_exists('curl_init')) {
|
if ($content === false && function_exists('curl_init')) {
|
||||||
$ch = curl_init(self::RELEASE_URL);
|
$ch = curl_init($url);
|
||||||
curl_setopt_array($ch, [
|
curl_setopt_array($ch, [
|
||||||
CURLOPT_RETURNTRANSFER => true,
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
CURLOPT_FOLLOWLOCATION => true,
|
CURLOPT_FOLLOWLOCATION => true,
|
||||||
@@ -128,18 +209,7 @@ class MokoBridgeMigration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($content === false || strlen($content) < 1000) {
|
return $content;
|
||||||
self::log('Bridge: failed to download MokoOnyx ZIP from ' . self::RELEASE_URL, 'error');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file_put_contents($zipPath, $content) === false) {
|
|
||||||
self::log('Bridge: failed to write ZIP to ' . $zipPath, 'error');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
self::log('Bridge: downloaded MokoOnyx ZIP (' . strlen($content) . ' bytes)');
|
|
||||||
return $zipPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -113,15 +113,7 @@ class Tpl_MokocassiopeiaInstallerScript
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bridge migration: MokoCassiopeia → MokoOnyx
|
// Bridge migration runs in postflight() — not here — to avoid double execution
|
||||||
$bridgeScript = __DIR__ . '/helper/bridge.php';
|
|
||||||
if (is_file($bridgeScript)) {
|
|
||||||
require_once $bridgeScript;
|
|
||||||
if (class_exists('MokoBridgeMigration')) {
|
|
||||||
MokoBridgeMigration::run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user