From fbedd5966c0b1f4ae365cb9c8e6b0c3a0d1b5ecf Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Mon, 25 May 2026 23:09:45 -0500 Subject: [PATCH 1/3] fix(updates_xml): cascade entries down, fix Gitea release tag URLs, fix client tag - Cascade: when stable releases, write all 5 channel entries pointing to stable - Separate Joomla tags from Gitea release tags via releaseTagMap - Only add client tag for templates and modules, not packages - Preservation logic matches against Joomla tag names correctly Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- cli/updates_xml_build.php | 89 +++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 42 deletions(-) diff --git a/cli/updates_xml_build.php b/cli/updates_xml_build.php index 106d8e6..025945c 100644 --- a/cli/updates_xml_build.php +++ b/cli/updates_xml_build.php @@ -194,8 +194,7 @@ $stabilitySuffixMap = [ 'development' => '-dev', ]; -// Joomla's stabilityTagToInteger() maps these to STABILITY_* constants. -// MUST use 'dev' not 'development' — STABILITY_DEVELOPMENT does not exist. +// Joomla values — maps to Joomla's stabilityTagToInteger() $stabilityTagMap = [ 'stable' => 'stable', 'rc' => 'rc', @@ -204,23 +203,26 @@ $stabilityTagMap = [ 'development' => 'dev', ]; -// -- Build update entries ----------------------------------------------------- -$releaseTag = $stabilityTagMap[$stability] ?? $stability; +// Gitea release tag names (used in download/info URLs) +$releaseTagMap = [ + 'stable' => 'stable', + 'rc' => 'release-candidate', + 'beta' => 'beta', + 'alpha' => 'alpha', + 'development' => 'development', +]; +// -- Build update entries ----------------------------------------------------- // For the primary entry: apply suffix if not stable $primarySuffix = $stabilitySuffixMap[$stability] ?? ''; $primaryVersion = $version . $primarySuffix; -$downloadUrl = "{$giteaUrl}/{$org}/{$repo}/releases/download/{$releaseTag}/{$typePrefix}{$extElement}-{$primaryVersion}.zip"; -$infoUrl = "{$giteaUrl}/{$org}/{$repo}/releases/tag/{$releaseTag}"; - -// Build client tag — Joomla defaults to client_id=1 (administrator) when missing. -// Packages install with client_id=0 (site), so we MUST include site -// for all types to prevent a mismatch that causes extension_id=0 in #__updates. +// Build client tag — only needed for templates and modules (site vs admin). +// Packages and components don't use client; plugins use folder instead. $clientTag = ''; if (!empty($extClient)) { $clientTag = " {$extClient}"; -} else { +} elseif (in_array($extType, ['template', 'module'])) { $clientTag = ' site'; } @@ -286,41 +288,44 @@ function buildEntry( } // -- Determine which channels to write ---------------------------------------- -// Stable cascades to all channels; pre-releases only write their level and below -// Each channel gets its own suffixed version: -// development -> 04.01.00-dev -// alpha -> 04.01.00-alpha -// beta -> 04.01.00-beta -// rc -> 04.01.00-rc -// stable -> 04.01.00 +// Stable cascades to all channels; pre-releases cascade down to lower channels. +// Each channel entry represents "latest release available at this stability or higher". +// When stable releases, ALL channels point to stable (it's the newest for everyone). +// When RC releases, rc/beta/alpha/dev point to RC; stable is preserved. +// When dev releases, only dev is updated; everything else is preserved. $allChannels = ['development', 'alpha', 'beta', 'rc', 'stable']; $stabilityIndex = array_search($stability === 'development' ? 'development' : $stability, $allChannels); if ($stabilityIndex === false) $stabilityIndex = 4; // default to stable -// Write only the current channel entry (not cascade) -// Each channel release only creates its own entry; preserved entries handle other channels +// Write entries for the current channel AND all lower channels (cascade down) +// All cascaded entries point to the CURRENT release (the highest stability being built) $entries = []; -$channelName = $allChannels[$stabilityIndex]; -$channelSuffix = $stabilitySuffixMap[$channelName] ?? ''; -$channelVersion = $version . $channelSuffix; -$channelTag = $stabilityTagMap[$channelName] ?? $channelName; -$channelDownloadUrl = "{$giteaUrl}/{$org}/{$repo}/releases/download/{$channelTag}/{$typePrefix}{$extElement}-{$channelVersion}.zip"; -$channelInfoUrl = "{$giteaUrl}/{$org}/{$repo}/releases/tag/{$channelTag}"; +$giteaTag = $releaseTagMap[$stability] ?? $stability; +$channelVersion = $version . ($stabilitySuffixMap[$stability] ?? ''); +$channelDownloadUrl = "{$giteaUrl}/{$org}/{$repo}/releases/download/{$giteaTag}/{$typePrefix}{$extElement}-{$channelVersion}.zip"; +$channelInfoUrl = "{$giteaUrl}/{$org}/{$repo}/releases/tag/{$giteaTag}"; -$entries[] = buildEntry( - $channelName, - $channelVersion, - $channelDownloadUrl, - $extName, - $extElement, - $extType, - $clientTag, - $folderTag, - $channelInfoUrl, - $targetPlatform, - $phpTag, - $shaTag -); +for ($i = 0; $i <= $stabilityIndex; $i++) { + $channelName = $allChannels[$i]; + $joomlaTag = $stabilityTagMap[$channelName] ?? $channelName; + // Only attach SHA to the primary channel entry + $entrySha = ($i === $stabilityIndex) ? $shaTag : ''; + + $entries[] = buildEntry( + $joomlaTag, + $channelVersion, + $channelDownloadUrl, + $extName, + $extElement, + $extType, + $clientTag, + $folderTag, + $channelInfoUrl, + $targetPlatform, + $phpTag, + $entrySha + ); +} // -- Preserve existing entries for channels not being updated ----------------- $dest = $outputFile ?? "{$root}/updates.xml"; @@ -329,10 +334,10 @@ $preservedEntries = []; if (file_exists($dest)) { $existingXml = @simplexml_load_file($dest); if ($existingXml) { - // Channels we're writing — don't preserve these + // Joomla tags we're writing — don't preserve these $writtenChannels = []; for ($i = 0; $i <= $stabilityIndex; $i++) { - $writtenChannels[] = $allChannels[$i]; + $writtenChannels[] = $stabilityTagMap[$allChannels[$i]] ?? $allChannels[$i]; } foreach ($existingXml->update as $existingUpdate) { From 83842c50ad1b313427ac8861ac22598062226d69 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Mon, 25 May 2026 23:12:12 -0500 Subject: [PATCH 2/3] docs(changelog): add updates_xml_build fixes to Unreleased Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea7f500..91f8c63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,12 @@ Version format: `XX.YY.ZZ` (zero-padded semver). ## [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 `site` 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 ### Changed From 3f3b1f79a0028dd35db35f582b89298bc6df0256 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Mon, 25 May 2026 23:26:07 -0500 Subject: [PATCH 3/3] chore: add PHPDoc to Priority 1 Enterprise classes Added @since, @param, @see tags to: - CliFramework: class-level @since, 2 undocumented methods - GitHubAdapter: class @since/@see, constructor @param, property docs - MokoGiteaAdapter: class @since/@see, constructor @param, property docs - ApiClient: class @since Wiki: created Coding-Standards page with full PHPDoc standard, PHPCS exclusion rationale, and file structure patterns. Partial progress on #137 Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/Enterprise/ApiClient.php | 2 ++ lib/Enterprise/CliFramework.php | 13 +++++++++++++ lib/Enterprise/GitHubAdapter.php | 7 ++++++- lib/Enterprise/MokoGiteaAdapter.php | 10 +++++++++- phpstan-baseline.neon | 10 ++-------- 5 files changed, 32 insertions(+), 10 deletions(-) diff --git a/lib/Enterprise/ApiClient.php b/lib/Enterprise/ApiClient.php index 8fb5f1a..eb6a369 100644 --- a/lib/Enterprise/ApiClient.php +++ b/lib/Enterprise/ApiClient.php @@ -92,6 +92,8 @@ class CircuitBreakerOpen extends RuntimeException * ); * $response = $client->get('/repos/owner/repo'); * ``` + * + * @since 04.00.00 */ class ApiClient { diff --git a/lib/Enterprise/CliFramework.php b/lib/Enterprise/CliFramework.php index f786ec5..52e08f9 100644 --- a/lib/Enterprise/CliFramework.php +++ b/lib/Enterprise/CliFramework.php @@ -716,6 +716,9 @@ class ValidationCLI extends CLIApp * Lifecycle: configure() -> parseArguments() -> printBanner() -> initialize() -> run() * * All new scripts must extend CliFramework and implement configure() + run(). + * + * @since 04.00.15 + * @see CLIApp Legacy base class (deprecated) */ abstract class CliFramework { @@ -932,6 +935,11 @@ abstract class CliFramework // Argument parsing (internal) // ========================================================================= + /** + * Parse CLI arguments from $_SERVER['argv'] into registered argument definitions. + * + * @since 04.00.15 + */ private function parseArguments(): void { $argv = array_slice($_SERVER['argv'] ?? [], 1); @@ -970,6 +978,11 @@ abstract class CliFramework // Help screen // ========================================================================= + /** + * Print auto-generated help screen from registered arguments. + * + * @since 04.00.15 + */ protected function printHelp(): void { $w = $this->termWidth(); diff --git a/lib/Enterprise/GitHubAdapter.php b/lib/Enterprise/GitHubAdapter.php index efddeb4..d34e701 100644 --- a/lib/Enterprise/GitHubAdapter.php +++ b/lib/Enterprise/GitHubAdapter.php @@ -32,12 +32,17 @@ use RuntimeException; * - Workflow dir: .github/workflows * * @package MokoStandards\Enterprise - * @version 04.06.10 + * @since 04.06.10 + * @see GitPlatformAdapter */ class GitHubAdapter implements GitPlatformAdapter { + /** @var ApiClient HTTP client for GitHub API calls. */ private ApiClient $apiClient; + /** + * @param ApiClient $apiClient Configured API client for api.github.com + */ public function __construct(ApiClient $apiClient) { $this->apiClient = $apiClient; diff --git a/lib/Enterprise/MokoGiteaAdapter.php b/lib/Enterprise/MokoGiteaAdapter.php index 9cc014d..79b0c5c 100644 --- a/lib/Enterprise/MokoGiteaAdapter.php +++ b/lib/Enterprise/MokoGiteaAdapter.php @@ -34,13 +34,21 @@ use RuntimeException; * - Workflow dir: .mokogitea/workflows * * @package MokoStandards\Enterprise - * @version 04.06.10 + * @since 04.06.10 + * @see GitPlatformAdapter */ class MokoGiteaAdapter implements GitPlatformAdapter { + /** @var ApiClient HTTP client for Gitea API calls. */ private ApiClient $apiClient; + + /** @var string Base URL for Gitea API (e.g. https://git.mokoconsulting.tech/api/v1). */ 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') { $this->apiClient = $apiClient; diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 3ee856b..4f77b05 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -415,15 +415,9 @@ parameters: 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 - count: 1 - 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 + count: 2 path: cli/updates_xml_build.php -