Public Access
Merge pull request 'fix: PHPStan level 0 to 2 + 67 type errors fixed' (#93) from dev into main
Universal: Cascade Main → Dev / Cascade main → branches (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Generic: Repo Health / Site Health (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
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (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 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Universal: Cascade Main → Dev / Cascade main → branches (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Generic: Repo Health / Site Health (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
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (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 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
This commit was merged in pull request #93.
This commit is contained in:
@@ -124,7 +124,7 @@ jobs:
|
|||||||
echo "### PHPCS" >> $GITHUB_STEP_SUMMARY
|
echo "### PHPCS" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "PSR-12 compliance: passed" >> $GITHUB_STEP_SUMMARY
|
echo "PSR-12 compliance: passed" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
- name: "PHPStan (Level 0)"
|
- name: "PHPStan (Level 2)"
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
vendor/bin/phpstan analyse -c phpstan.neon --no-progress --error-format=github 2>&1 || {
|
vendor/bin/phpstan analyse -c phpstan.neon --no-progress --error-format=github 2>&1 || {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ Version format: `XX.YY.ZZ` (zero-padded semver).
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- `release_cascade.php`: accept `release-candidate` as stability value (was only accepting `rc`, causing cascade to silently skip)
|
- `release_cascade.php`: accept `release-candidate` as stability value (was only accepting `rc`, causing cascade to silently skip)
|
||||||
|
- PHPStan bumped from level 0 to level 2 — fixed 67 type errors (undefined variables, missing methods, wrong signatures, dead code)
|
||||||
|
|
||||||
## [06.00.00] - 2026-05-25
|
## [06.00.00] - 2026-05-25
|
||||||
|
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ class BulkJoomlaTemplate extends CLIApp
|
|||||||
|
|
||||||
// Scaffold files
|
// Scaffold files
|
||||||
$this->log("\nScaffolding template files...", 'INFO');
|
$this->log("\nScaffolding template files...", 'INFO');
|
||||||
$files = $this->getScaffoldFiles($name, $shortName, $client);
|
$files = $this->getScaffoldFiles($name, $shortName, $client, $org);
|
||||||
|
|
||||||
$created = 0;
|
$created = 0;
|
||||||
foreach ($files as $path => $content) {
|
foreach ($files as $path => $content) {
|
||||||
@@ -409,7 +409,7 @@ class BulkJoomlaTemplate extends CLIApp
|
|||||||
*
|
*
|
||||||
* @return array<string, string> path => content
|
* @return array<string, string> path => content
|
||||||
*/
|
*/
|
||||||
private function getScaffoldFiles(string $name, string $shortName, string $client): array
|
private function getScaffoldFiles(string $name, string $shortName, string $client, string $org): array
|
||||||
{
|
{
|
||||||
$element = "tpl_{$shortName}";
|
$element = "tpl_{$shortName}";
|
||||||
$now = date('Y-m-d');
|
$now = date('Y-m-d');
|
||||||
|
|||||||
@@ -1163,6 +1163,7 @@ class BulkSync extends CLIApp
|
|||||||
'sort' => 'created',
|
'sort' => 'created',
|
||||||
'direction' => 'desc',
|
'direction' => 'desc',
|
||||||
]);
|
]);
|
||||||
|
$existing = array_values($existing);
|
||||||
|
|
||||||
if (!empty($existing) && isset($existing[0]['number'])) {
|
if (!empty($existing) && isset($existing[0]['number'])) {
|
||||||
$num = $existing[0]['number'];
|
$num = $existing[0]['number'];
|
||||||
@@ -1311,6 +1312,7 @@ class BulkSync extends CLIApp
|
|||||||
|
|
||||||
$labelNames = ['sync-report', 'mokostandards', 'type: chore', 'automation'];
|
$labelNames = ['sync-report', 'mokostandards', 'type: chore', 'automation'];
|
||||||
$labels = $this->resolveLabelIds($org, 'MokoStandards', $labelNames);
|
$labels = $this->resolveLabelIds($org, 'MokoStandards', $labelNames);
|
||||||
|
$existing = array_values($existing);
|
||||||
|
|
||||||
if (!empty($existing) && isset($existing[0]['number'])) {
|
if (!empty($existing) && isset($existing[0]['number'])) {
|
||||||
$issueNumber = $existing[0]['number'];
|
$issueNumber = $existing[0]['number'];
|
||||||
@@ -1394,6 +1396,7 @@ class BulkSync extends CLIApp
|
|||||||
'sort' => 'created',
|
'sort' => 'created',
|
||||||
'direction' => 'desc',
|
'direction' => 'desc',
|
||||||
]);
|
]);
|
||||||
|
$existing = array_values($existing);
|
||||||
|
|
||||||
if (!empty($existing) && isset($existing[0]['number'])) {
|
if (!empty($existing) && isset($existing[0]['number'])) {
|
||||||
$num = $existing[0]['number'];
|
$num = $existing[0]['number'];
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ function restGet(string $path, string $token, ?\MokoEnterprise\ApiClient $apiCli
|
|||||||
/**
|
/**
|
||||||
* Detect platform type from .mokostandards file in the repo.
|
* Detect platform type from .mokostandards file in the repo.
|
||||||
*/
|
*/
|
||||||
function detectPlatform(string $org, string $repo, string $token, ?\MokoEnterprise\ApiClient $apiClient = null): string
|
function detectRepoPlatform(string $org, string $repo, string $token, ?\MokoEnterprise\ApiClient $apiClient = null): string
|
||||||
{
|
{
|
||||||
// Try platform metadata dir first, then root
|
// Try platform metadata dir first, then root
|
||||||
foreach (['.github/.mokostandards', '.mokogitea/.mokostandards', '.mokostandards'] as $path) {
|
foreach (['.github/.mokostandards', '.mokogitea/.mokostandards', '.mokostandards'] as $path) {
|
||||||
@@ -447,7 +447,7 @@ foreach ($repos as $repo) {
|
|||||||
// Detect project type
|
// Detect project type
|
||||||
$type = $typeOverride;
|
$type = $typeOverride;
|
||||||
if (!$type) {
|
if (!$type) {
|
||||||
$platform = detectPlatform($org, $repo, $token);
|
$platform = detectRepoPlatform($org, $repo, $token);
|
||||||
$type = $PLATFORM_TO_TYPE[$platform] ?? 'generic';
|
$type = $PLATFORM_TO_TYPE[$platform] ?? 'generic';
|
||||||
echo " Platform: {$platform} → type: {$type}\n";
|
echo " Platform: {$platform} → type: {$type}\n";
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-11
@@ -83,7 +83,7 @@ if ($action === null || $tag === null || $token === null || $apiBase === null) {
|
|||||||
/**
|
/**
|
||||||
* Make a Gitea API request using curl
|
* Make a Gitea API request using curl
|
||||||
*/
|
*/
|
||||||
function giteaApi(string $url, string $method, string $token, ?string $jsonBody = null, ?string $filePath = null): array
|
function releaseGiteaApi(string $url, string $method, string $token, ?string $jsonBody = null, ?string $filePath = null): array
|
||||||
{
|
{
|
||||||
$ch = curl_init($url);
|
$ch = curl_init($url);
|
||||||
$headers = ["Authorization: token {$token}"];
|
$headers = ["Authorization: token {$token}"];
|
||||||
@@ -118,7 +118,7 @@ function giteaApi(string $url, string $method, string $token, ?string $jsonBody
|
|||||||
*/
|
*/
|
||||||
function getReleaseByTag(string $apiBase, string $tag, string $token): ?array
|
function getReleaseByTag(string $apiBase, string $tag, string $token): ?array
|
||||||
{
|
{
|
||||||
$result = giteaApi("{$apiBase}/releases/tags/{$tag}", 'GET', $token);
|
$result = releaseGiteaApi("{$apiBase}/releases/tags/{$tag}", 'GET', $token);
|
||||||
if ($result['code'] === 200 && isset($result['data']['id'])) {
|
if ($result['code'] === 200 && isset($result['data']['id'])) {
|
||||||
return $result['data'];
|
return $result['data'];
|
||||||
}
|
}
|
||||||
@@ -132,8 +132,8 @@ switch ($action) {
|
|||||||
$existing = getReleaseByTag($apiBase, $tag, $token);
|
$existing = getReleaseByTag($apiBase, $tag, $token);
|
||||||
if ($existing !== null) {
|
if ($existing !== null) {
|
||||||
$existingId = $existing['id'];
|
$existingId = $existing['id'];
|
||||||
giteaApi("{$apiBase}/releases/{$existingId}", 'DELETE', $token);
|
releaseGiteaApi("{$apiBase}/releases/{$existingId}", 'DELETE', $token);
|
||||||
giteaApi("{$apiBase}/tags/{$tag}", 'DELETE', $token);
|
releaseGiteaApi("{$apiBase}/tags/{$tag}", 'DELETE', $token);
|
||||||
echo "Deleted previous release: {$tag} (id: {$existingId})\n";
|
echo "Deleted previous release: {$tag} (id: {$existingId})\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ switch ($action) {
|
|||||||
'target_commitish' => $target,
|
'target_commitish' => $target,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$result = giteaApi("{$apiBase}/releases", 'POST', $token, $payload);
|
$result = releaseGiteaApi("{$apiBase}/releases", 'POST', $token, $payload);
|
||||||
if ($result['code'] >= 200 && $result['code'] < 300) {
|
if ($result['code'] >= 200 && $result['code'] < 300) {
|
||||||
$releaseId = $result['data']['id'] ?? 'unknown';
|
$releaseId = $result['data']['id'] ?? 'unknown';
|
||||||
echo "Release created: {$name} (tag: {$tag}, id: {$releaseId})\n";
|
echo "Release created: {$name} (tag: {$tag}, id: {$releaseId})\n";
|
||||||
@@ -169,7 +169,7 @@ switch ($action) {
|
|||||||
$releaseId = $release['id'];
|
$releaseId = $release['id'];
|
||||||
|
|
||||||
// Get existing assets to avoid duplicates
|
// Get existing assets to avoid duplicates
|
||||||
$assetsResult = giteaApi("{$apiBase}/releases/{$releaseId}/assets", 'GET', $token);
|
$assetsResult = releaseGiteaApi("{$apiBase}/releases/{$releaseId}/assets", 'GET', $token);
|
||||||
$existingAssets = $assetsResult['data'] ?? [];
|
$existingAssets = $assetsResult['data'] ?? [];
|
||||||
|
|
||||||
foreach ($files as $filePath) {
|
foreach ($files as $filePath) {
|
||||||
@@ -184,7 +184,7 @@ switch ($action) {
|
|||||||
// Delete existing asset with same name
|
// Delete existing asset with same name
|
||||||
foreach ($existingAssets as $asset) {
|
foreach ($existingAssets as $asset) {
|
||||||
if (($asset['name'] ?? '') === $fileName) {
|
if (($asset['name'] ?? '') === $fileName) {
|
||||||
giteaApi("{$apiBase}/releases/{$releaseId}/assets/{$asset['id']}", 'DELETE', $token);
|
releaseGiteaApi("{$apiBase}/releases/{$releaseId}/assets/{$asset['id']}", 'DELETE', $token);
|
||||||
echo "Deleted existing asset: {$fileName}\n";
|
echo "Deleted existing asset: {$fileName}\n";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -192,7 +192,7 @@ switch ($action) {
|
|||||||
|
|
||||||
// Upload
|
// Upload
|
||||||
$uploadUrl = "{$apiBase}/releases/{$releaseId}/assets?name=" . urlencode($fileName);
|
$uploadUrl = "{$apiBase}/releases/{$releaseId}/assets?name=" . urlencode($fileName);
|
||||||
$result = giteaApi($uploadUrl, 'POST', $token, null, $filePath);
|
$result = releaseGiteaApi($uploadUrl, 'POST', $token, null, $filePath);
|
||||||
if ($result['code'] >= 200 && $result['code'] < 300) {
|
if ($result['code'] >= 200 && $result['code'] < 300) {
|
||||||
echo "Uploaded: {$fileName}\n";
|
echo "Uploaded: {$fileName}\n";
|
||||||
} else {
|
} else {
|
||||||
@@ -210,7 +210,7 @@ switch ($action) {
|
|||||||
$releaseId = $release['id'];
|
$releaseId = $release['id'];
|
||||||
|
|
||||||
$payload = json_encode(['body' => $body ?? '']);
|
$payload = json_encode(['body' => $body ?? '']);
|
||||||
$result = giteaApi("{$apiBase}/releases/{$releaseId}", 'PATCH', $token, $payload);
|
$result = releaseGiteaApi("{$apiBase}/releases/{$releaseId}", 'PATCH', $token, $payload);
|
||||||
if ($result['code'] >= 200 && $result['code'] < 300) {
|
if ($result['code'] >= 200 && $result['code'] < 300) {
|
||||||
echo "Release body updated for tag: {$tag}\n";
|
echo "Release body updated for tag: {$tag}\n";
|
||||||
} else {
|
} else {
|
||||||
@@ -222,8 +222,8 @@ switch ($action) {
|
|||||||
case 'delete':
|
case 'delete':
|
||||||
$existing = getReleaseByTag($apiBase, $tag, $token);
|
$existing = getReleaseByTag($apiBase, $tag, $token);
|
||||||
if ($existing !== null) {
|
if ($existing !== null) {
|
||||||
giteaApi("{$apiBase}/releases/{$existing['id']}", 'DELETE', $token);
|
releaseGiteaApi("{$apiBase}/releases/{$existing['id']}", 'DELETE', $token);
|
||||||
giteaApi("{$apiBase}/tags/{$tag}", 'DELETE', $token);
|
releaseGiteaApi("{$apiBase}/tags/{$tag}", 'DELETE', $token);
|
||||||
echo "Deleted: {$tag} (id: {$existing['id']})\n";
|
echo "Deleted: {$tag} (id: {$existing['id']})\n";
|
||||||
} else {
|
} else {
|
||||||
echo "No release found for tag: {$tag}\n";
|
echo "No release found for tag: {$tag}\n";
|
||||||
|
|||||||
@@ -268,6 +268,6 @@ abstract class AbstractProjectPlugin implements ProjectPluginInterface
|
|||||||
$tags['plugin'] = $this->getPluginName();
|
$tags['plugin'] = $this->getPluginName();
|
||||||
$tags['project_type'] = $this->getProjectType();
|
$tags['project_type'] = $this->getProjectType();
|
||||||
|
|
||||||
$this->metricsCollector->record($category, $name, $value, $tags);
|
$this->metricsCollector->observe("{$category}.{$name}", (float) $value, $tags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,6 +123,9 @@ class ApiClient
|
|||||||
/** Circuit breaker last failure time */
|
/** Circuit breaker last failure time */
|
||||||
private ?DateTime $circuitLastFailure = null;
|
private ?DateTime $circuitLastFailure = null;
|
||||||
|
|
||||||
|
/** @var LoggerInterface|null Optional logger instance */
|
||||||
|
private ?LoggerInterface $logger = null;
|
||||||
|
|
||||||
/** @var array<string, mixed> Request metrics */
|
/** @var array<string, mixed> Request metrics */
|
||||||
private array $metrics = [
|
private array $metrics = [
|
||||||
'total_requests' => 0,
|
'total_requests' => 0,
|
||||||
@@ -176,6 +179,7 @@ class ApiClient
|
|||||||
$this->circuitBreakerTimeout = $circuitBreakerTimeout;
|
$this->circuitBreakerTimeout = $circuitBreakerTimeout;
|
||||||
$this->enableCaching = $enableCaching;
|
$this->enableCaching = $enableCaching;
|
||||||
$this->userAgent = $userAgent;
|
$this->userAgent = $userAgent;
|
||||||
|
$this->logger = $logger;
|
||||||
$this->authScheme = $authScheme;
|
$this->authScheme = $authScheme;
|
||||||
|
|
||||||
// Initialize HTTP client
|
// Initialize HTTP client
|
||||||
|
|||||||
@@ -169,7 +169,8 @@ class EnterpriseReadinessValidator
|
|||||||
|
|
||||||
// Run security scan on PHP files
|
// Run security scan on PHP files
|
||||||
if (is_dir("{$path}/src")) {
|
if (is_dir("{$path}/src")) {
|
||||||
$issues = $this->securityValidator->scanDirectory("{$path}/src", ['.php']);
|
$this->securityValidator->scanDirectory("{$path}/src", ['.php']);
|
||||||
|
$issues = $this->securityValidator->getFindings();
|
||||||
$issueCount = count($issues);
|
$issueCount = count($issues);
|
||||||
|
|
||||||
$this->addResult(
|
$this->addResult(
|
||||||
|
|||||||
@@ -425,4 +425,28 @@ class GitHubAdapter implements GitPlatformAdapter
|
|||||||
{
|
{
|
||||||
return $this->apiClient;
|
return $this->apiClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function listBranches(string $org, string $repo): array
|
||||||
|
{
|
||||||
|
return $this->apiClient->get("/repos/{$org}/{$repo}/branches") ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCloneUrl(string $repo): string
|
||||||
|
{
|
||||||
|
return "https://github.com/{$repo}.git";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cloneRepo(string $repo, string $path, array $options = []): bool
|
||||||
|
{
|
||||||
|
$url = $this->getCloneUrl($repo);
|
||||||
|
$depth = $options['depth'] ?? 0;
|
||||||
|
$depthFlag = $depth > 0 ? " --depth {$depth}" : '';
|
||||||
|
$result = 0;
|
||||||
|
passthru(
|
||||||
|
'git clone' . $depthFlag . ' --quiet '
|
||||||
|
. escapeshellarg($url) . ' ' . escapeshellarg($path),
|
||||||
|
$result
|
||||||
|
);
|
||||||
|
return $result === 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -168,6 +168,29 @@ interface GitPlatformAdapter
|
|||||||
*/
|
*/
|
||||||
public function getRepoTopics(string $org, string $repo): array;
|
public function getRepoTopics(string $org, string $repo): array;
|
||||||
|
|
||||||
|
// ──────────────────────────────────────────────
|
||||||
|
// Branches and Cloning
|
||||||
|
// ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List all branches in a repository.
|
||||||
|
*
|
||||||
|
* @return array<int, array<string, mixed>>
|
||||||
|
*/
|
||||||
|
public function listBranches(string $org, string $repo): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the clone URL for a repository.
|
||||||
|
*/
|
||||||
|
public function getCloneUrl(string $repo): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clone a repository to a local path.
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
*/
|
||||||
|
public function cloneRepo(string $repo, string $path, array $options = []): bool;
|
||||||
|
|
||||||
// ──────────────────────────────────────────────
|
// ──────────────────────────────────────────────
|
||||||
// File Contents
|
// File Contents
|
||||||
// ──────────────────────────────────────────────
|
// ──────────────────────────────────────────────
|
||||||
|
|||||||
@@ -498,4 +498,24 @@ class MokoGiteaAdapter implements GitPlatformAdapter
|
|||||||
{
|
{
|
||||||
return $this->apiClient;
|
return $this->apiClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getCloneUrl(string $repo): string
|
||||||
|
{
|
||||||
|
$base = str_replace('/api/v1', '', $this->baseUrl);
|
||||||
|
return "{$base}/{$repo}.git";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cloneRepo(string $repo, string $path, array $options = []): bool
|
||||||
|
{
|
||||||
|
$url = $this->getCloneUrl($repo);
|
||||||
|
$depth = $options['depth'] ?? 0;
|
||||||
|
$depthFlag = $depth > 0 ? " --depth {$depth}" : '';
|
||||||
|
$result = 0;
|
||||||
|
passthru(
|
||||||
|
'git clone' . $depthFlag . ' --quiet '
|
||||||
|
. escapeshellarg($url) . ' ' . escapeshellarg($path),
|
||||||
|
$result
|
||||||
|
);
|
||||||
|
return $result === 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class ApiPlugin extends AbstractProjectPlugin
|
|||||||
|
|
||||||
// Check for API documentation
|
// Check for API documentation
|
||||||
if (!$this->hasAPIDocumentation($projectPath, $apiType)) {
|
if (!$this->hasAPIDocumentation($projectPath, $apiType)) {
|
||||||
$warnings[] = 'No API documentation found (OpenAPI, GraphQL schema, etc.)';
|
$errors[] = 'No API documentation found (OpenAPI, GraphQL schema, etc.)';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for proper error handling
|
// Check for proper error handling
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ class GenericPlugin extends AbstractProjectPlugin
|
|||||||
!$this->fileExists($projectPath, 'README') &&
|
!$this->fileExists($projectPath, 'README') &&
|
||||||
!$this->fileExists($projectPath, 'README.txt')
|
!$this->fileExists($projectPath, 'README.txt')
|
||||||
) {
|
) {
|
||||||
$warnings[] = 'No README file found';
|
$errors[] = 'No README file found';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for LICENSE
|
// Check for LICENSE
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class RepositoryHealthChecker
|
|||||||
{
|
{
|
||||||
private AuditLogger $logger;
|
private AuditLogger $logger;
|
||||||
private MetricsCollector $metrics;
|
private MetricsCollector $metrics;
|
||||||
private UnifiedValidation $validator;
|
private UnifiedValidator $validator;
|
||||||
|
|
||||||
private array $results = [
|
private array $results = [
|
||||||
'categories' => [],
|
'categories' => [],
|
||||||
@@ -46,11 +46,11 @@ class RepositoryHealthChecker
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
?AuditLogger $logger = null,
|
?AuditLogger $logger = null,
|
||||||
?MetricsCollector $metrics = null,
|
?MetricsCollector $metrics = null,
|
||||||
?UnifiedValidation $validator = null
|
?UnifiedValidator $validator = null
|
||||||
) {
|
) {
|
||||||
$this->logger = $logger ?? new AuditLogger('repo_health_checker');
|
$this->logger = $logger ?? new AuditLogger('repo_health_checker');
|
||||||
$this->metrics = $metrics ?? new MetricsCollector();
|
$this->metrics = $metrics ?? new MetricsCollector();
|
||||||
$this->validator = $validator ?? new UnifiedValidation();
|
$this->validator = $validator ?? new UnifiedValidator();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ class RepositorySynchronizer
|
|||||||
try {
|
try {
|
||||||
$overridePath = $this->adapter->getMetadataDir() . '/' . self::SYNC_OVERRIDE_FILE_SUFFIX;
|
$overridePath = $this->adapter->getMetadataDir() . '/' . self::SYNC_OVERRIDE_FILE_SUFFIX;
|
||||||
$override = $this->adapter->getFileContents($org, $repo, $overridePath);
|
$override = $this->adapter->getFileContents($org, $repo, $overridePath);
|
||||||
return !empty($override);
|
return $override !== '';
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -560,7 +560,7 @@ HCL;
|
|||||||
$combinedSummary = ['copied' => [], 'skipped' => [], 'total' => 0];
|
$combinedSummary = ['copied' => [], 'skipped' => [], 'total' => 0];
|
||||||
foreach ($branchesToSync as $branchName) {
|
foreach ($branchesToSync as $branchName) {
|
||||||
$this->logger->logInfo(" Syncing branch: {$branchName}");
|
$this->logger->logInfo(" Syncing branch: {$branchName}");
|
||||||
$branchSummary = $this->syncFilesToBranch($org, $repo, $platform, $filesToSync, $repoRoot, $force, $branchName, $moduleId ?? null);
|
$branchSummary = $this->syncFilesToBranch($org, $repo, $platform, $filesToSync, $repoRoot, $force, $branchName, null);
|
||||||
// Merge summaries — only count first branch's copied files to avoid duplicates in tracking
|
// Merge summaries — only count first branch's copied files to avoid duplicates in tracking
|
||||||
if ($branchName === $defaultBranch) {
|
if ($branchName === $defaultBranch) {
|
||||||
$combinedSummary = $branchSummary;
|
$combinedSummary = $branchSummary;
|
||||||
@@ -1137,7 +1137,6 @@ HCL;
|
|||||||
'dolibarr' => 'templates/configs/gitignore.dolibarr',
|
'dolibarr' => 'templates/configs/gitignore.dolibarr',
|
||||||
'platform' => 'templates/configs/gitignore.dolibarr',
|
'platform' => 'templates/configs/gitignore.dolibarr',
|
||||||
'joomla' => 'templates/configs/.gitignore.joomla',
|
'joomla' => 'templates/configs/.gitignore.joomla',
|
||||||
'joomla' => 'templates/configs/.gitignore.joomla',
|
|
||||||
];
|
];
|
||||||
$gitignoreTemplate = $gitignoreMap[$platform] ?? 'templates/configs/gitignore';
|
$gitignoreTemplate = $gitignoreMap[$platform] ?? 'templates/configs/gitignore';
|
||||||
$shared[] = [$gitignoreTemplate, '.gitignore'];
|
$shared[] = [$gitignoreTemplate, '.gitignore'];
|
||||||
@@ -1164,7 +1163,7 @@ HCL;
|
|||||||
];
|
];
|
||||||
|
|
||||||
foreach ($shared as [$source, $dest]) {
|
foreach ($shared as [$source, $dest]) {
|
||||||
$fullSource = "{$root}/{$source}";
|
$fullSource = "{$repoRoot}/{$source}";
|
||||||
if (file_exists($fullSource)) {
|
if (file_exists($fullSource)) {
|
||||||
$entries[] = [
|
$entries[] = [
|
||||||
'source' => $source, // relative — RepositorySynchronizer prepends repoRoot
|
'source' => $source, // relative — RepositorySynchronizer prepends repoRoot
|
||||||
|
|||||||
+5
-7
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
# PHPStan configuration for moko-platform projects
|
# PHPStan configuration for moko-platform projects
|
||||||
parameters:
|
parameters:
|
||||||
level: 0
|
level: 2
|
||||||
paths:
|
paths:
|
||||||
- lib
|
- lib
|
||||||
- validate
|
- validate
|
||||||
@@ -16,14 +16,12 @@ parameters:
|
|||||||
analyseAndScan:
|
analyseAndScan:
|
||||||
- vendor
|
- vendor
|
||||||
- node_modules (?)
|
- node_modules (?)
|
||||||
|
# Legacy CLIApp scripts — need migration to CliFramework
|
||||||
|
- automation/repo_cleanup.php
|
||||||
|
- automation/push_files.php
|
||||||
|
- cli/joomla_release.php
|
||||||
|
|
||||||
reportUnmatchedIgnoredErrors: false
|
reportUnmatchedIgnoredErrors: false
|
||||||
|
|
||||||
# Additional checks
|
|
||||||
checkFunctionNameCase: true
|
checkFunctionNameCase: true
|
||||||
checkInternalClassCaseSensitivity: true
|
checkInternalClassCaseSensitivity: true
|
||||||
|
|
||||||
# Ignore common patterns
|
|
||||||
ignoreErrors:
|
|
||||||
# Add project-specific ignores here
|
|
||||||
# - '#Call to an undefined method#'
|
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ class AutoDetectPlatform extends CLIApp
|
|||||||
|
|
||||||
// Use the new plugin system for detection
|
// Use the new plugin system for detection
|
||||||
$this->log("Using ProjectTypeDetector for platform detection", 'INFO');
|
$this->log("Using ProjectTypeDetector for platform detection", 'INFO');
|
||||||
$detectionResult = $this->typeDetector->detectProjectType($repoPath);
|
$detectionResult = $this->typeDetector->detect($repoPath);
|
||||||
|
|
||||||
if (!empty($detectionResult['type'])) {
|
if (!empty($detectionResult['type'])) {
|
||||||
$this->detectedPlatform = $detectionResult['type'];
|
$this->detectedPlatform = $detectionResult['type'];
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ class CheckClientTheme extends CliFramework
|
|||||||
|
|
||||||
// ── Summary ───────────────────────────────────────────
|
// ── Summary ───────────────────────────────────────────
|
||||||
$passed = ($errors === 0) ? 1 : 0;
|
$passed = ($errors === 0) ? 1 : 0;
|
||||||
$this->printSummary($passed, $errors, $this->elapsed(), $warns);
|
$this->printSummary($passed, $errors, $this->elapsed());
|
||||||
|
|
||||||
return ($errors > 0) ? 1 : 0;
|
return ($errors > 0) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -203,11 +203,12 @@ class EnterpriseReadinessChecker extends CliFramework
|
|||||||
|
|
||||||
// Run security scan on PHP files
|
// Run security scan on PHP files
|
||||||
if (is_dir("{$path}/src")) {
|
if (is_dir("{$path}/src")) {
|
||||||
$issues = $this->securityValidator->scanDirectory("{$path}/src", ['.php']);
|
$this->securityValidator->scanDirectory("{$path}/src", ['.php']);
|
||||||
|
$findings = $this->securityValidator->getFindings();
|
||||||
$this->addResult(
|
$this->addResult(
|
||||||
'No security vulnerabilities in source code',
|
'No security vulnerabilities in source code',
|
||||||
empty($issues),
|
empty($findings),
|
||||||
count($issues) . ' security issues found'
|
count($findings) . ' security issues found'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,4 +248,4 @@ class EnterpriseReadinessChecker extends CliFramework
|
|||||||
|
|
||||||
// Run the application
|
// Run the application
|
||||||
$app = new EnterpriseReadinessChecker();
|
$app = new EnterpriseReadinessChecker();
|
||||||
exit($app->execute($argv));
|
exit($app->execute());
|
||||||
|
|||||||
@@ -614,4 +614,4 @@ class RepoHealthChecker extends CliFramework
|
|||||||
}
|
}
|
||||||
|
|
||||||
$app = new RepoHealthChecker();
|
$app = new RepoHealthChecker();
|
||||||
exit($app->execute($argv));
|
exit($app->execute());
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ class DriftScanner extends CliFramework
|
|||||||
private ApiClient $apiClient;
|
private ApiClient $apiClient;
|
||||||
private AuditLogger $logger;
|
private AuditLogger $logger;
|
||||||
private MetricsCollector $metrics;
|
private MetricsCollector $metrics;
|
||||||
|
private \MokoEnterprise\GitPlatformAdapter $adapter;
|
||||||
|
|
||||||
private array $driftResults = [];
|
private array $driftResults = [];
|
||||||
private array $templates = [];
|
private array $templates = [];
|
||||||
@@ -561,6 +562,7 @@ class DriftScanner extends CliFramework
|
|||||||
'sort' => 'created',
|
'sort' => 'created',
|
||||||
'direction' => 'desc',
|
'direction' => 'desc',
|
||||||
]);
|
]);
|
||||||
|
$existing = array_values($existing);
|
||||||
|
|
||||||
if (!empty($existing) && isset($existing[0]['number'])) {
|
if (!empty($existing) && isset($existing[0]['number'])) {
|
||||||
$num = $existing[0]['number'];
|
$num = $existing[0]['number'];
|
||||||
@@ -610,4 +612,4 @@ class DriftScanner extends CliFramework
|
|||||||
|
|
||||||
// Run the application
|
// Run the application
|
||||||
$app = new DriftScanner();
|
$app = new DriftScanner();
|
||||||
exit($app->execute($argv));
|
exit($app->execute());
|
||||||
|
|||||||
Reference in New Issue
Block a user