cbfa23c4c4
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Scripts governance (push) Successful in 5s
Generic: Repo Health / Release configuration (push) Successful in 5s
Generic: Repo Health / Repository health (push) Successful in 12s
Platform: moko-platform CI / Gate 1: Code Quality (push) Successful in 45s
Platform: moko-platform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 4s
Universal: PR Check / Validate PR (pull_request) Successful in 5s
Universal: PR Check / Build RC Package (pull_request) Successful in 2s
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Failing after 44s
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Failing after 48s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Failing after 48s
Platform: moko-platform CI / Gate 4: Governance (push) Successful in 48s
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Failing after 50s
Platform: moko-platform CI / Gate 5: Template Integrity (push) Failing after 12s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Successful in 1m13s
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Failing after 5s
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Failing after 42s
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Failing after 45s
Platform: moko-platform CI / Gate 4: Governance (pull_request) Successful in 44s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Failing after 47s
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Failing after 49s
Real bugs found and fixed: - bulk_joomla_template: $org undefined in heredoc (missing parameter) - RepositorySynchronizer: $root undefined (should be $repoRoot), duplicate array key - RepositoryHealthChecker: wrong class name (UnifiedValidation → UnifiedValidator) - scan_drift: missing $adapter property declaration - auto_detect_platform: wrong method name (detectProjectType → detect) - EnterpriseReadinessValidator: void return used as value - check_client_theme: extra parameter to printSummary() - ApiClient: unused constructor parameter now stored - GitPlatformAdapter: added listBranches/getCloneUrl/cloneRepo to interface - MokoGiteaAdapter/GitHubAdapter: implemented new interface methods 3 legacy CLIApp scripts excluded (need migration to CliFramework): repo_cleanup.php, push_files.php, joomla_release.php Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
274 lines
7.4 KiB
PHP
274 lines
7.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/* 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: MokoStandards.Enterprise.Plugins
|
|
* INGROUP: MokoStandards.Enterprise
|
|
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
|
* PATH: /lib/Enterprise/AbstractProjectPlugin.php
|
|
* BRIEF: Abstract base class for project plugins
|
|
*/
|
|
|
|
namespace MokoEnterprise;
|
|
|
|
/**
|
|
* Abstract base class for project type plugins
|
|
*
|
|
* Provides common functionality for all project type plugins
|
|
*
|
|
* @package MokoStandards\Enterprise
|
|
* @version 1.0.0
|
|
*/
|
|
abstract class AbstractProjectPlugin implements ProjectPluginInterface
|
|
{
|
|
/** @var AuditLogger */
|
|
protected $logger;
|
|
|
|
/** @var MetricsCollector */
|
|
protected $metricsCollector;
|
|
|
|
/** @var array Plugin configuration */
|
|
protected $config;
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param AuditLogger|null $logger Optional audit logger
|
|
* @param MetricsCollector|null $metricsCollector Optional metrics collector
|
|
* @param array $config Plugin configuration
|
|
*/
|
|
public function __construct(
|
|
?AuditLogger $logger = null,
|
|
?MetricsCollector $metricsCollector = null,
|
|
array $config = []
|
|
) {
|
|
$this->logger = $logger ?? new AuditLogger('project_plugin');
|
|
$this->metricsCollector = $metricsCollector ?? new MetricsCollector();
|
|
$this->config = $config;
|
|
}
|
|
|
|
/**
|
|
* {@inheritdoc}
|
|
*/
|
|
abstract public function getProjectType(): string;
|
|
|
|
/**
|
|
* {@inheritdoc}
|
|
*/
|
|
abstract public function getPluginName(): string;
|
|
|
|
/**
|
|
* {@inheritdoc}
|
|
*/
|
|
public function getPluginVersion(): string
|
|
{
|
|
return '1.0.0';
|
|
}
|
|
|
|
/**
|
|
* {@inheritdoc}
|
|
*/
|
|
abstract public function validateProject(array $config, string $projectPath): array;
|
|
|
|
/**
|
|
* {@inheritdoc}
|
|
*/
|
|
abstract public function collectMetrics(string $projectPath, array $config): array;
|
|
|
|
/**
|
|
* {@inheritdoc}
|
|
*/
|
|
abstract public function healthCheck(string $projectPath, array $config): array;
|
|
|
|
/**
|
|
* {@inheritdoc}
|
|
*/
|
|
abstract public function getRequiredFiles(): array;
|
|
|
|
/**
|
|
* {@inheritdoc}
|
|
*/
|
|
abstract public function getRecommendedFiles(): array;
|
|
|
|
/**
|
|
* {@inheritdoc}
|
|
*/
|
|
abstract public function getConfigSchema(): array;
|
|
|
|
/**
|
|
* {@inheritdoc}
|
|
*/
|
|
abstract public function getBestPractices(): array;
|
|
|
|
/**
|
|
* {@inheritdoc}
|
|
*/
|
|
public function checkReadiness(string $projectPath, array $config): array
|
|
{
|
|
$validation = $this->validateProject($config, $projectPath);
|
|
$health = $this->healthCheck($projectPath, $config);
|
|
|
|
$blockers = array_merge(
|
|
$validation['errors'] ?? [],
|
|
array_filter($health['issues'] ?? [], function ($issue) {
|
|
return ($issue['severity'] ?? '') === 'critical';
|
|
})
|
|
);
|
|
|
|
$warnings = array_merge(
|
|
$validation['warnings'] ?? [],
|
|
array_filter($health['issues'] ?? [], function ($issue) {
|
|
return ($issue['severity'] ?? '') === 'warning';
|
|
})
|
|
);
|
|
|
|
return [
|
|
'ready' => empty($blockers),
|
|
'blockers' => $blockers,
|
|
'warnings' => $warnings,
|
|
'score' => $health['score'] ?? 0,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* {@inheritdoc}
|
|
*/
|
|
public function getCommands(): array
|
|
{
|
|
// Default: no custom commands
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* {@inheritdoc}
|
|
*/
|
|
public function initializeProject(string $projectPath, array $options = []): array
|
|
{
|
|
// Default: no initialization
|
|
return [
|
|
'success' => true,
|
|
'message' => 'No initialization required for ' . $this->getProjectType(),
|
|
'files_created' => [],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Check if a file exists in the project
|
|
*
|
|
* @param string $projectPath Project directory path
|
|
* @param string $filePath Relative file path
|
|
* @return bool True if file exists
|
|
*/
|
|
protected function fileExists(string $projectPath, string $filePath): bool
|
|
{
|
|
return file_exists(rtrim($projectPath, '/') . '/' . ltrim($filePath, '/'));
|
|
}
|
|
|
|
/**
|
|
* Read a file from the project
|
|
*
|
|
* @param string $projectPath Project directory path
|
|
* @param string $filePath Relative file path
|
|
* @return string|null File contents or null if not found
|
|
*/
|
|
protected function readFile(string $projectPath, string $filePath): ?string
|
|
{
|
|
$fullPath = rtrim($projectPath, '/') . '/' . ltrim($filePath, '/');
|
|
return file_exists($fullPath) ? file_get_contents($fullPath) : null;
|
|
}
|
|
|
|
/**
|
|
* Check if files match a pattern in the project
|
|
*
|
|
* @param string $projectPath Project directory path
|
|
* @param string $pattern Glob pattern
|
|
* @return array Matching file paths
|
|
*/
|
|
protected function findFiles(string $projectPath, string $pattern): array
|
|
{
|
|
$fullPattern = rtrim($projectPath, '/') . '/' . ltrim($pattern, '/');
|
|
$matches = glob($fullPattern);
|
|
return is_array($matches) ? $matches : [];
|
|
}
|
|
|
|
/**
|
|
* Count files matching a pattern
|
|
*
|
|
* @param string $projectPath Project directory path
|
|
* @param string $pattern Glob pattern
|
|
* @return int Number of matching files
|
|
*/
|
|
protected function countFiles(string $projectPath, string $pattern): int
|
|
{
|
|
return count($this->findFiles($projectPath, $pattern));
|
|
}
|
|
|
|
/**
|
|
* Parse JSON file
|
|
*
|
|
* @param string $projectPath Project directory path
|
|
* @param string $filePath Relative file path
|
|
* @return array|null Parsed JSON data or null on error
|
|
*/
|
|
protected function parseJsonFile(string $projectPath, string $filePath): ?array
|
|
{
|
|
$content = $this->readFile($projectPath, $filePath);
|
|
if ($content === null) {
|
|
return null;
|
|
}
|
|
|
|
$data = json_decode($content, true);
|
|
return json_last_error() === JSON_ERROR_NONE ? $data : null;
|
|
}
|
|
|
|
/**
|
|
* Log plugin activity
|
|
*
|
|
* @param string $message Log message
|
|
* @param string $level Log level (info, warning, error)
|
|
* @param array $context Additional context
|
|
* @return void
|
|
*/
|
|
protected function log(string $message, string $level = 'info', array $context = []): void
|
|
{
|
|
$context['plugin'] = $this->getPluginName();
|
|
$context['project_type'] = $this->getProjectType();
|
|
|
|
switch ($level) {
|
|
case 'error':
|
|
$this->logger->logError($message, $context);
|
|
break;
|
|
case 'warning':
|
|
$this->logger->logWarning($message, $context);
|
|
break;
|
|
default:
|
|
$this->logger->logInfo($message, $context);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Record metrics
|
|
*
|
|
* @param string $category Metric category
|
|
* @param string $name Metric name
|
|
* @param mixed $value Metric value
|
|
* @param array $tags Optional tags
|
|
* @return void
|
|
*/
|
|
protected function recordMetric(string $category, string $name, $value, array $tags = []): void
|
|
{
|
|
$tags['plugin'] = $this->getPluginName();
|
|
$tags['project_type'] = $this->getProjectType();
|
|
|
|
$this->metricsCollector->observe("{$category}.{$name}", (float) $value, $tags);
|
|
}
|
|
}
|