Public Access
b73c1eba25
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Platform: mokoplatform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Universal: Security Audit / Dependency Audit (pull_request) Successful in 8s
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Failing after 11s
Universal: PR Check / Validate PR (pull_request) Successful in 11s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 21s
Generic: Project CI / Lint & Validate (pull_request) Failing after 32s
Platform: mokoplatform CI / Gate 1: Code Quality (pull_request) Failing after 1m31s
Scans source files to detect platform, name, version, element_name, package_type, language, entry_point, description, and license_spdx. Supports Joomla, Dolibarr, Go, MCP/Node, and generic platforms. Includes --diff and --update modes for comparing against and pushing to the Gitea manifest API. Warns on missing core fields. Also removes deprecated mcp/servers/mokowaas_api (consolidated to separate repo) and syncs dev branch changes.
188 lines
6.0 KiB
PHP
188 lines
6.0 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: MokoPlatform.Enterprise
|
|
* INGROUP: MokoPlatform.Lib
|
|
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
|
|
* PATH: /lib/Enterprise/SourceResolver.php
|
|
* BRIEF: Resolve the root-level source directory across repos (source/, src/, htdocs/)
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace MokoEnterprise;
|
|
|
|
/**
|
|
* Source Directory Resolver
|
|
*
|
|
* Provides a single, consistent fallback chain for locating the root-level
|
|
* source directory in any MokoStandards repository. The preferred directory
|
|
* is `source/`, with legacy `src/` and `htdocs/` as fallbacks.
|
|
*
|
|
* This class exists because Joomla extensions use `src/` for namespace
|
|
* autoloading (e.g. administrator/components/com_foo/src/). Renaming our
|
|
* root-level source directory to `source/` avoids that collision. During
|
|
* the transition period, repos may still use `src/`, so all tooling must
|
|
* check both.
|
|
*
|
|
* Usage:
|
|
* $dir = SourceResolver::resolve($repoRoot); // 'source', 'src', or 'htdocs'
|
|
* $abs = SourceResolver::resolveAbsolute($repoRoot); // full path or null
|
|
* $xmls = SourceResolver::globSource($repoRoot, '*.xml'); // glob under first match
|
|
* $path = SourceResolver::findUnderSource($repoRoot, 'core/modules'); // subpath lookup
|
|
*
|
|
* @since 09.02.00
|
|
*/
|
|
class SourceResolver
|
|
{
|
|
/**
|
|
* Ordered candidate directories. source/ is preferred, src/ is legacy fallback.
|
|
*
|
|
* When the migration is complete and all repos use source/, the 'src'
|
|
* entry can be removed from this list.
|
|
*
|
|
* @var string[]
|
|
*/
|
|
private const CANDIDATES = ['source', 'src', 'htdocs'];
|
|
|
|
/**
|
|
* Resolve the source directory name for a repository root.
|
|
*
|
|
* Returns the first candidate directory that exists, or 'source' as the
|
|
* default when no candidate is found (e.g. for new repos being scaffolded).
|
|
*
|
|
* @param string $root Absolute path to the repository root.
|
|
* @return string Directory name (e.g. 'source', 'src', 'htdocs').
|
|
*/
|
|
public static function resolve(string $root): string
|
|
{
|
|
foreach (self::CANDIDATES as $candidate) {
|
|
if (is_dir("{$root}/{$candidate}")) {
|
|
return $candidate;
|
|
}
|
|
}
|
|
|
|
return 'source';
|
|
}
|
|
|
|
/**
|
|
* Resolve the source directory as an absolute path.
|
|
*
|
|
* @param string $root Absolute path to the repository root.
|
|
* @return string|null Absolute path to the source directory, or null if none exists.
|
|
*/
|
|
public static function resolveAbsolute(string $root): ?string
|
|
{
|
|
foreach (self::CANDIDATES as $candidate) {
|
|
$path = "{$root}/{$candidate}";
|
|
if (is_dir($path)) {
|
|
return $path;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Glob for files under the source directory.
|
|
*
|
|
* Checks each candidate directory in order and returns matches from the
|
|
* first candidate that produces results. This replaces patterns like:
|
|
*
|
|
* glob("{$root}/src/*.xml")
|
|
*
|
|
* With the backwards-compatible:
|
|
*
|
|
* SourceResolver::globSource($root, '*.xml')
|
|
*
|
|
* @param string $root Absolute path to the repository root.
|
|
* @param string $pattern Glob pattern relative to the source directory.
|
|
* @return string[] Matched file paths (may be empty).
|
|
*/
|
|
public static function globSource(string $root, string $pattern): array
|
|
{
|
|
foreach (self::CANDIDATES as $candidate) {
|
|
$dir = "{$root}/{$candidate}";
|
|
if (!is_dir($dir)) {
|
|
continue;
|
|
}
|
|
$matches = glob("{$dir}/{$pattern}") ?: [];
|
|
if ($matches !== []) {
|
|
return $matches;
|
|
}
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* Find a subpath under any source directory candidate.
|
|
*
|
|
* Useful for locating platform-specific subdirectories like
|
|
* `core/modules/` (Dolibarr) or `media/templates/` (Joomla client themes)
|
|
* regardless of whether the repo uses `source/` or `src/`.
|
|
*
|
|
* @param string $root Absolute path to the repository root.
|
|
* @param string $subpath Relative path to look for (e.g. 'core/modules', 'index.ts').
|
|
* @return string|null Absolute path if found, null otherwise.
|
|
*/
|
|
public static function findUnderSource(string $root, string $subpath): ?string
|
|
{
|
|
foreach (self::CANDIDATES as $candidate) {
|
|
$full = "{$root}/{$candidate}/{$subpath}";
|
|
if (file_exists($full) || is_dir($full)) {
|
|
return $full;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get the ordered list of candidate directory names.
|
|
*
|
|
* Useful for workflows or scripts that need to iterate candidates
|
|
* themselves (e.g. building find/grep patterns).
|
|
*
|
|
* @return string[]
|
|
*/
|
|
public static function getCandidates(): array
|
|
{
|
|
return self::CANDIDATES;
|
|
}
|
|
|
|
/**
|
|
* Check whether the resolved source directory is a legacy name (src/).
|
|
*
|
|
* @param string $root Absolute path to the repository root.
|
|
* @return bool True if the repo uses src/ instead of source/.
|
|
*/
|
|
public static function isLegacy(string $root): bool
|
|
{
|
|
$resolved = self::resolve($root);
|
|
|
|
return $resolved === 'src';
|
|
}
|
|
|
|
/**
|
|
* Emit a deprecation warning to stderr if the repo still uses src/.
|
|
*
|
|
* CLI tools should call this after resolving the source directory so
|
|
* that maintainers know to rename src/ → source/.
|
|
*
|
|
* @param string $root Absolute path to the repository root.
|
|
*/
|
|
public static function warnIfLegacy(string $root): void
|
|
{
|
|
if (self::isLegacy($root)) {
|
|
fwrite(STDERR, "⚠ WARNING: This repo uses src/ which is deprecated. Rename to source/ per MokoStandards.\n");
|
|
}
|
|
}
|
|
}
|