Merge pull request 'chore: PHPDoc Priority 1 + Coding Standards wiki' (#138) from dev into main
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Universal: Cascade Main → Dev / Cascade main → branches (push) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (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 / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (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

This commit was merged in pull request #138.
This commit is contained in:
2026-05-26 04:29:29 +00:00
7 changed files with 85 additions and 52 deletions
+6
View File
@@ -18,6 +18,12 @@ Version format: `XX.YY.ZZ` (zero-padded semver).
## [Unreleased] ## [Unreleased]
### Fixed
- `updates_xml_build.php`: cascade entries down to lower channels — stable now writes all 5 entries instead of wiping them
- `updates_xml_build.php`: separate Joomla stability tags (`dev`, `rc`) from Gitea release tags (`development`, `release-candidate`) — download URLs now point to correct release assets
- `updates_xml_build.php`: only emit `<client>site</client>` for templates and modules, not packages or components
- `updates_xml_build.php`: preservation logic matches Joomla tag names when deciding which existing entries to keep
## [08.00.00] - 2026-05-26 ## [08.00.00] - 2026-05-26
### Changed ### Changed
+47 -42
View File
@@ -194,8 +194,7 @@ $stabilitySuffixMap = [
'development' => '-dev', 'development' => '-dev',
]; ];
// Joomla's stabilityTagToInteger() maps these to STABILITY_* constants. // Joomla <tags><tag> values — maps to Joomla's stabilityTagToInteger()
// MUST use 'dev' not 'development' — STABILITY_DEVELOPMENT does not exist.
$stabilityTagMap = [ $stabilityTagMap = [
'stable' => 'stable', 'stable' => 'stable',
'rc' => 'rc', 'rc' => 'rc',
@@ -204,23 +203,26 @@ $stabilityTagMap = [
'development' => 'dev', 'development' => 'dev',
]; ];
// -- Build update entries ----------------------------------------------------- // Gitea release tag names (used in download/info URLs)
$releaseTag = $stabilityTagMap[$stability] ?? $stability; $releaseTagMap = [
'stable' => 'stable',
'rc' => 'release-candidate',
'beta' => 'beta',
'alpha' => 'alpha',
'development' => 'development',
];
// -- Build update entries -----------------------------------------------------
// For the primary entry: apply suffix if not stable // For the primary entry: apply suffix if not stable
$primarySuffix = $stabilitySuffixMap[$stability] ?? ''; $primarySuffix = $stabilitySuffixMap[$stability] ?? '';
$primaryVersion = $version . $primarySuffix; $primaryVersion = $version . $primarySuffix;
$downloadUrl = "{$giteaUrl}/{$org}/{$repo}/releases/download/{$releaseTag}/{$typePrefix}{$extElement}-{$primaryVersion}.zip"; // Build client tag — only needed for templates and modules (site vs admin).
$infoUrl = "{$giteaUrl}/{$org}/{$repo}/releases/tag/{$releaseTag}"; // Packages and components don't use client; plugins use folder instead.
// Build client tag — Joomla defaults to client_id=1 (administrator) when missing.
// Packages install with client_id=0 (site), so we MUST include <client>site</client>
// for all types to prevent a mismatch that causes extension_id=0 in #__updates.
$clientTag = ''; $clientTag = '';
if (!empty($extClient)) { if (!empty($extClient)) {
$clientTag = " <client>{$extClient}</client>"; $clientTag = " <client>{$extClient}</client>";
} else { } elseif (in_array($extType, ['template', 'module'])) {
$clientTag = ' <client>site</client>'; $clientTag = ' <client>site</client>';
} }
@@ -286,41 +288,44 @@ function buildEntry(
} }
// -- Determine which channels to write ---------------------------------------- // -- Determine which channels to write ----------------------------------------
// Stable cascades to all channels; pre-releases only write their level and below // Stable cascades to all channels; pre-releases cascade down to lower channels.
// Each channel gets its own suffixed version: // Each channel entry represents "latest release available at this stability or higher".
// development -> 04.01.00-dev // When stable releases, ALL channels point to stable (it's the newest for everyone).
// alpha -> 04.01.00-alpha // When RC releases, rc/beta/alpha/dev point to RC; stable is preserved.
// beta -> 04.01.00-beta // When dev releases, only dev is updated; everything else is preserved.
// rc -> 04.01.00-rc
// stable -> 04.01.00
$allChannels = ['development', 'alpha', 'beta', 'rc', 'stable']; $allChannels = ['development', 'alpha', 'beta', 'rc', 'stable'];
$stabilityIndex = array_search($stability === 'development' ? 'development' : $stability, $allChannels); $stabilityIndex = array_search($stability === 'development' ? 'development' : $stability, $allChannels);
if ($stabilityIndex === false) $stabilityIndex = 4; // default to stable if ($stabilityIndex === false) $stabilityIndex = 4; // default to stable
// Write only the current channel entry (not cascade) // Write entries for the current channel AND all lower channels (cascade down)
// Each channel release only creates its own entry; preserved entries handle other channels // All cascaded entries point to the CURRENT release (the highest stability being built)
$entries = []; $entries = [];
$channelName = $allChannels[$stabilityIndex]; $giteaTag = $releaseTagMap[$stability] ?? $stability;
$channelSuffix = $stabilitySuffixMap[$channelName] ?? ''; $channelVersion = $version . ($stabilitySuffixMap[$stability] ?? '');
$channelVersion = $version . $channelSuffix; $channelDownloadUrl = "{$giteaUrl}/{$org}/{$repo}/releases/download/{$giteaTag}/{$typePrefix}{$extElement}-{$channelVersion}.zip";
$channelTag = $stabilityTagMap[$channelName] ?? $channelName; $channelInfoUrl = "{$giteaUrl}/{$org}/{$repo}/releases/tag/{$giteaTag}";
$channelDownloadUrl = "{$giteaUrl}/{$org}/{$repo}/releases/download/{$channelTag}/{$typePrefix}{$extElement}-{$channelVersion}.zip";
$channelInfoUrl = "{$giteaUrl}/{$org}/{$repo}/releases/tag/{$channelTag}";
$entries[] = buildEntry( for ($i = 0; $i <= $stabilityIndex; $i++) {
$channelName, $channelName = $allChannels[$i];
$channelVersion, $joomlaTag = $stabilityTagMap[$channelName] ?? $channelName;
$channelDownloadUrl, // Only attach SHA to the primary channel entry
$extName, $entrySha = ($i === $stabilityIndex) ? $shaTag : '';
$extElement,
$extType, $entries[] = buildEntry(
$clientTag, $joomlaTag,
$folderTag, $channelVersion,
$channelInfoUrl, $channelDownloadUrl,
$targetPlatform, $extName,
$phpTag, $extElement,
$shaTag $extType,
); $clientTag,
$folderTag,
$channelInfoUrl,
$targetPlatform,
$phpTag,
$entrySha
);
}
// -- Preserve existing entries for channels not being updated ----------------- // -- Preserve existing entries for channels not being updated -----------------
$dest = $outputFile ?? "{$root}/updates.xml"; $dest = $outputFile ?? "{$root}/updates.xml";
@@ -329,10 +334,10 @@ $preservedEntries = [];
if (file_exists($dest)) { if (file_exists($dest)) {
$existingXml = @simplexml_load_file($dest); $existingXml = @simplexml_load_file($dest);
if ($existingXml) { if ($existingXml) {
// Channels we're writing — don't preserve these // Joomla tags we're writing — don't preserve these
$writtenChannels = []; $writtenChannels = [];
for ($i = 0; $i <= $stabilityIndex; $i++) { for ($i = 0; $i <= $stabilityIndex; $i++) {
$writtenChannels[] = $allChannels[$i]; $writtenChannels[] = $stabilityTagMap[$allChannels[$i]] ?? $allChannels[$i];
} }
foreach ($existingXml->update as $existingUpdate) { foreach ($existingXml->update as $existingUpdate) {
+2
View File
@@ -92,6 +92,8 @@ class CircuitBreakerOpen extends RuntimeException
* ); * );
* $response = $client->get('/repos/owner/repo'); * $response = $client->get('/repos/owner/repo');
* ``` * ```
*
* @since 04.00.00
*/ */
class ApiClient class ApiClient
{ {
+13
View File
@@ -716,6 +716,9 @@ class ValidationCLI extends CLIApp
* Lifecycle: configure() -> parseArguments() -> printBanner() -> initialize() -> run() * Lifecycle: configure() -> parseArguments() -> printBanner() -> initialize() -> run()
* *
* All new scripts must extend CliFramework and implement configure() + run(). * All new scripts must extend CliFramework and implement configure() + run().
*
* @since 04.00.15
* @see CLIApp Legacy base class (deprecated)
*/ */
abstract class CliFramework abstract class CliFramework
{ {
@@ -932,6 +935,11 @@ abstract class CliFramework
// Argument parsing (internal) // Argument parsing (internal)
// ========================================================================= // =========================================================================
/**
* Parse CLI arguments from $_SERVER['argv'] into registered argument definitions.
*
* @since 04.00.15
*/
private function parseArguments(): void private function parseArguments(): void
{ {
$argv = array_slice($_SERVER['argv'] ?? [], 1); $argv = array_slice($_SERVER['argv'] ?? [], 1);
@@ -970,6 +978,11 @@ abstract class CliFramework
// Help screen // Help screen
// ========================================================================= // =========================================================================
/**
* Print auto-generated help screen from registered arguments.
*
* @since 04.00.15
*/
protected function printHelp(): void protected function printHelp(): void
{ {
$w = $this->termWidth(); $w = $this->termWidth();
+6 -1
View File
@@ -32,12 +32,17 @@ use RuntimeException;
* - Workflow dir: .github/workflows * - Workflow dir: .github/workflows
* *
* @package MokoStandards\Enterprise * @package MokoStandards\Enterprise
* @version 04.06.10 * @since 04.06.10
* @see GitPlatformAdapter
*/ */
class GitHubAdapter implements GitPlatformAdapter class GitHubAdapter implements GitPlatformAdapter
{ {
/** @var ApiClient HTTP client for GitHub API calls. */
private ApiClient $apiClient; private ApiClient $apiClient;
/**
* @param ApiClient $apiClient Configured API client for api.github.com
*/
public function __construct(ApiClient $apiClient) public function __construct(ApiClient $apiClient)
{ {
$this->apiClient = $apiClient; $this->apiClient = $apiClient;
+9 -1
View File
@@ -34,13 +34,21 @@ use RuntimeException;
* - Workflow dir: .mokogitea/workflows * - Workflow dir: .mokogitea/workflows
* *
* @package MokoStandards\Enterprise * @package MokoStandards\Enterprise
* @version 04.06.10 * @since 04.06.10
* @see GitPlatformAdapter
*/ */
class MokoGiteaAdapter implements GitPlatformAdapter class MokoGiteaAdapter implements GitPlatformAdapter
{ {
/** @var ApiClient HTTP client for Gitea API calls. */
private ApiClient $apiClient; private ApiClient $apiClient;
/** @var string Base URL for Gitea API (e.g. https://git.mokoconsulting.tech/api/v1). */
private string $baseUrl; private string $baseUrl;
/**
* @param ApiClient $apiClient Configured API client
* @param string $baseUrl Gitea API base URL
*/
public function __construct(ApiClient $apiClient, string $baseUrl = 'https://git.mokoconsulting.tech/api/v1') public function __construct(ApiClient $apiClient, string $baseUrl = 'https://git.mokoconsulting.tech/api/v1')
{ {
$this->apiClient = $apiClient; $this->apiClient = $apiClient;
+2 -8
View File
@@ -415,15 +415,9 @@ parameters:
path: cli/theme_lint.php path: cli/theme_lint.php
- -
message: '#^Offset ''alpha''\|''beta''\|''development''\|''rc''\|''stable'' on array\{stable\: '''', rc\: ''\-rc'', beta\: ''\-beta'', alpha\: ''\-alpha'', development\: ''\-dev''\} on left side of \?\? always exists and is not nullable\.$#' message: '#^Offset ''alpha''\|''beta''\|''development''\|''rc''\|''stable'' on array\{stable\: ''stable'', rc\: ''rc'', beta\: ''beta'', alpha\: ''alpha'', development\: ''dev''\} on left side of \?\? always exists and is not nullable\.$#'
identifier: nullCoalesce.offset identifier: nullCoalesce.offset
count: 1 count: 2
path: cli/updates_xml_build.php
-
message: '#^Offset ''alpha''\|''beta''\|''development''\|''rc''\|''stable'' on array\{stable\: ''stable'', rc\: ''rc'', beta\: ''beta'', alpha\: ''alpha'', development\: ''development''\} on left side of \?\? always exists and is not nullable\.$#'
identifier: nullCoalesce.offset
count: 1
path: cli/updates_xml_build.php path: cli/updates_xml_build.php
- -