Files
MokoCLI/lib/Enterprise/PlatformAdapterFactory.php
T
jmiller 50f253d444 chore(rebrand): gitea -> git (MokoGitea -> MokoGit, .mokogitea -> .mokogit)
Case-preserving gitea->git across the mokocli monorepo: brand text,
functional identifiers (MOKOGITEA_TOKEN, GITEA_URL/ORG/REPO, --gitea-url),
and all nested .mokogitea/ dirs (top-level + mcp/servers/*) -> .mokogit/.
Protected: literal `.gitea` (upstream workflow-detection paths in the
Enterprise plugins) and git.mokoconsulting.tech.

WARNING: CI-breaking until the MokoGit server side is reconfigured.

Claude-Session: https://claude.ai/code/session_01DQEMmJPe61ya7HDfA6BHP8
2026-07-14 15:56:17 -05:00

196 lines
6.6 KiB
PHP

<?php
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
*
* This file is part of a Moko Consulting project.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* FILE INFORMATION
* DEFGROUP: MokoCLI.Enterprise.Platform
* INGROUP: MokoCLI.Enterprise
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /lib/Enterprise/PlatformAdapterFactory.php
* BRIEF: Factory for creating platform-specific GitPlatformAdapter instances
*/
declare(strict_types=1);
namespace MokoCli;
use RuntimeException;
/**
* Factory for creating GitPlatformAdapter instances.
*
* Reads GIT_PLATFORM env var (default: 'github') and constructs
* the appropriate adapter with correct base URL, auth scheme, and token.
*
* Usage:
* ```php
* $config = Config::load();
* $adapter = PlatformAdapterFactory::create($config);
* $repos = $adapter->listOrgRepos('mokoconsulting-tech');
* ```
*
* @package MokoCLI\Enterprise
* @version 04.06.10
*
* @since 04.00.00
*/
class PlatformAdapterFactory
{
/**
* Create a GitPlatformAdapter based on configuration.
*
* @param Config $config Configuration instance
* @param string|null $platformOverride Force a specific platform ('github' or 'git')
* @return GitPlatformAdapter The constructed adapter
* @throws RuntimeException If the platform is not supported or token is missing
*/
public static function create(Config $config, ?string $platformOverride = null): GitPlatformAdapter
{
$platform = $platformOverride ?? $config->getString('platform', 'git');
return match ($platform) {
'github' => self::createGitHubAdapter($config),
'git' => self::createMokoGitAdapter($config),
default => throw new RuntimeException("Unsupported git platform: {$platform}. Use 'github' or 'git'."),
};
}
/**
* Create a GitHubAdapter with configured ApiClient.
*
* @param Config $config Configuration instance
* @return GitHubAdapter Configured GitHub adapter
* @throws RuntimeException If GitHub token is not available
*/
private static function createGitHubAdapter(Config $config): GitHubAdapter
{
$token = $config->getString('github.token', '');
if (empty($token)) {
throw new RuntimeException(
'GitHub token not found. Set GH_TOKEN, GITHUB_TOKEN, or authenticate with `gh auth login`.'
);
}
$apiClient = new ApiClient(
baseUrl: 'https://git.mokoconsulting.tech/api/v1',
authToken: $token,
maxRequestsPerHour: $config->getInt('github.rate_limit', 5000),
maxRetries: $config->getInt('github.max_retries', 3),
authScheme: 'Bearer'
);
return new GitHubAdapter($apiClient);
}
/**
* Create a MokoGitAdapter with configured ApiClient.
*
* @param Config $config Configuration instance
* @return MokoGitAdapter Configured Git adapter
* @throws RuntimeException If Git token is not available
*/
private static function createMokoGitAdapter(Config $config): MokoGitAdapter
{
$token = $config->getString('git.token', '');
if (empty($token)) {
throw new RuntimeException(
'Git token not found. Set GA_TOKEN environment variable.'
);
}
$gitUrl = $config->getString('git.url', 'https://git.mokoconsulting.tech');
$apiBaseUrl = rtrim($gitUrl, '/') . '/api/v1';
$apiClient = new ApiClient(
baseUrl: $apiBaseUrl,
authToken: $token,
maxRequestsPerHour: $config->getInt('git.rate_limit', 5000),
maxRetries: $config->getInt('git.max_retries', 3),
authScheme: 'token'
);
return new MokoGitAdapter($apiClient, $apiBaseUrl);
}
/**
* Create adapters for both platforms (useful during migration).
*
* @param Config $config Configuration instance
* @return array{github: GitHubAdapter, git: MokoGitAdapter} Both adapters
* @throws RuntimeException If either token is missing
*/
public static function createBoth(Config $config): array
{
return [
'github' => self::createGitHubAdapter($config),
'git' => self::createMokoGitAdapter($config),
];
}
/**
* Sync a file between Git (primary) and GitHub (mirror) for a given repo.
*
* Reads the file from Git and pushes it to GitHub, ensuring both platforms
* serve identical content. Commonly used for updates.xml sync after releases.
*
* @param Config $config Configuration instance
* @param string $repo Repository name
* @param string $branch Branch to sync (default: 'main')
* @param string $filePath Path to the file (default: 'updates.xml')
* @return bool True if sync succeeded or file was already identical
* @throws RuntimeException If either platform is unreachable
*/
public static function syncUpdatesBetweenPlatforms(
Config $config,
string $repo,
string $branch = 'main',
string $filePath = 'updates.xml'
): bool {
$adapters = self::createBoth($config);
$gitOrg = $config->getString('git.organization', 'mokoconsulting-tech');
$githubOrg = $config->getString('github.organization', 'mokoconsulting-tech');
// Read from Git (primary)
try {
$gitFile = $adapters['git']->getFileContents($gitOrg, $repo, $filePath, $branch);
} catch (\Exception $e) {
throw new RuntimeException("Failed to read {$filePath} from Git ({$gitOrg}/{$repo}): " . $e->getMessage());
}
$gitContent = base64_decode($gitFile['content'] ?? '');
if (empty($gitContent)) {
return false;
}
// Read from GitHub (mirror) to check if update is needed
$githubSha = null;
try {
$githubFile = $adapters['github']->getFileContents($githubOrg, $repo, $filePath, $branch);
$githubContent = base64_decode($githubFile['content'] ?? '');
$githubSha = $githubFile['sha'] ?? null;
if ($githubContent === $gitContent) {
return true;
}
} catch (\Exception $e) {
$adapters['github']->getApiClient()->resetCircuitBreaker();
}
$adapters['github']->createOrUpdateFile(
$githubOrg,
$repo,
$filePath,
$gitContent,
"chore(sync): sync {$filePath} from Git primary",
$githubSha,
$branch
);
return true;
}
}