mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-07-07 08:45:40 +08:00
Separate package config
This commit is contained in:
@@ -77,4 +77,19 @@ class ArtifactConfig
|
||||
{
|
||||
return self::$artifact_configs[$artifact_name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an inline artifact configuration.
|
||||
* Used when artifact is defined inline within a package configuration.
|
||||
*
|
||||
* @param string $artifact_name Artifact name (usually same as package name)
|
||||
* @param array $config Artifact configuration
|
||||
* @param string $registry_name Registry name
|
||||
* @param string $source_info Source info for debugging
|
||||
*/
|
||||
public static function registerInlineArtifact(string $artifact_name, array $config, string $registry_name, string $source_info = 'inline'): void
|
||||
{
|
||||
self::$artifact_configs[$artifact_name] = $config;
|
||||
Registry::_bindArtifactConfigFile($artifact_name, $registry_name, $source_info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ class ConfigValidator
|
||||
'type' => ConfigType::STRING,
|
||||
'depends' => ConfigType::LIST_ARRAY, // @
|
||||
'suggests' => ConfigType::LIST_ARRAY, // @
|
||||
'artifact' => ConfigType::STRING,
|
||||
'artifact' => [self::class, 'validateArtifactField'], // STRING or OBJECT
|
||||
'license' => [ConfigType::class, 'validateLicenseField'],
|
||||
'lang' => ConfigType::STRING,
|
||||
'frameworks' => ConfigType::LIST_ARRAY, // @
|
||||
@@ -102,7 +102,14 @@ class ConfigValidator
|
||||
if (!is_array($data)) {
|
||||
throw new ValidationException("{$config_file_name} is broken");
|
||||
}
|
||||
|
||||
// Define allowed artifact fields
|
||||
$allowed_artifact_fields = ['source', 'source-mirror', 'binary', 'binary-mirror', 'metadata'];
|
||||
|
||||
foreach ($data as $name => $artifact) {
|
||||
// First pass: validate unknown fields
|
||||
self::validateNoInvalidFields('artifact', $name, $artifact, $allowed_artifact_fields);
|
||||
|
||||
foreach ($artifact as $k => $v) {
|
||||
// check source field
|
||||
if ($k === 'source' || $k === 'source-mirror') {
|
||||
@@ -202,6 +209,11 @@ class ConfigValidator
|
||||
throw new ValidationException("Package [{$name}] in {$config_file_name} of type '{$pkg['type']}' must have an 'artifact' field");
|
||||
}
|
||||
|
||||
// validate and lint inline artifact object if present
|
||||
if (isset($pkg['artifact']) && is_array($pkg['artifact'])) {
|
||||
self::validateAndLintInlineArtifact($name, $data[$name]['artifact']);
|
||||
}
|
||||
|
||||
// check if "php-extension" package has php-extension specific fields and validate inside
|
||||
if ($pkg['type'] === 'php-extension') {
|
||||
self::validatePhpExtensionFields($name, $pkg);
|
||||
@@ -234,6 +246,19 @@ class ConfigValidator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate artifact field - can be string (reference) or object (inline).
|
||||
*
|
||||
* @param mixed $value Field value
|
||||
*/
|
||||
public static function validateArtifactField(mixed $value): bool
|
||||
{
|
||||
if (!is_string($value) && !is_assoc_array($value)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate an artifact download object field.
|
||||
*
|
||||
@@ -373,4 +398,19 @@ class ConfigValidator
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and lint inline artifact object structure.
|
||||
*
|
||||
* @param string $pkg_name Package name
|
||||
* @param array $artifact Inline artifact configuration (passed by reference to apply linting)
|
||||
*/
|
||||
private static function validateAndLintInlineArtifact(string $pkg_name, array &$artifact): void
|
||||
{
|
||||
// Validate and lint as if it's a standalone artifact
|
||||
$temp_data = [$pkg_name => $artifact];
|
||||
self::validateAndLintArtifacts("inline artifact in package '{$pkg_name}'", $temp_data);
|
||||
// Write back the linted artifact configuration
|
||||
$artifact = $temp_data[$pkg_name];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class PackageConfig
|
||||
throw new WrongUsageException("Directory {$dir} does not exist, cannot load pkg.json config.");
|
||||
}
|
||||
$loaded = [];
|
||||
$files = glob("{$dir}/pkg.*.json");
|
||||
$files = glob("{$dir}/*");
|
||||
if (is_array($files)) {
|
||||
foreach ($files as $file) {
|
||||
self::loadFromFile($file, $registry_name);
|
||||
@@ -58,10 +58,39 @@ class PackageConfig
|
||||
foreach ($data as $pkg_name => $config) {
|
||||
self::$package_configs[$pkg_name] = $config;
|
||||
Registry::_bindPackageConfigFile($pkg_name, $registry_name, $file);
|
||||
|
||||
// Register inline artifact if present
|
||||
if (isset($config['artifact']) && is_array($config['artifact'])) {
|
||||
ArtifactConfig::registerInlineArtifact(
|
||||
$pkg_name,
|
||||
$config['artifact'],
|
||||
$registry_name,
|
||||
"inline in {$file}"
|
||||
);
|
||||
}
|
||||
}
|
||||
return $file;
|
||||
}
|
||||
|
||||
public static function loadFromArray(array $data, string $registry_name): void
|
||||
{
|
||||
ConfigValidator::validateAndLintPackages('array_input', $data);
|
||||
foreach ($data as $pkg_name => $config) {
|
||||
self::$package_configs[$pkg_name] = $config;
|
||||
Registry::_bindPackageConfigFile($pkg_name, $registry_name, 'array_input');
|
||||
|
||||
// Register inline artifact if present
|
||||
if (isset($config['artifact']) && is_array($config['artifact'])) {
|
||||
ArtifactConfig::registerInlineArtifact(
|
||||
$pkg_name,
|
||||
$config['artifact'],
|
||||
$registry_name,
|
||||
'inline in array_input'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a package configuration exists.
|
||||
*/
|
||||
|
||||
@@ -19,7 +19,6 @@ class LinuxToolCheck
|
||||
'bzip2', 'cmake', 'gcc',
|
||||
'g++', 'patch', 'binutils-gold',
|
||||
'libtoolize', 'which',
|
||||
'patchelf',
|
||||
];
|
||||
|
||||
public const TOOLS_DEBIAN = [
|
||||
@@ -28,7 +27,6 @@ class LinuxToolCheck
|
||||
'tar', 'unzip', 'gzip', 'gcc', 'g++',
|
||||
'bzip2', 'cmake', 'patch',
|
||||
'xz', 'libtoolize', 'which',
|
||||
'patchelf',
|
||||
];
|
||||
|
||||
public const TOOLS_RHEL = [
|
||||
@@ -36,8 +34,7 @@ class LinuxToolCheck
|
||||
'git', 'autoconf', 'automake',
|
||||
'tar', 'unzip', 'gzip', 'gcc', 'g++',
|
||||
'bzip2', 'cmake', 'patch', 'which',
|
||||
'xz', 'libtool', 'gettext-devel',
|
||||
'patchelf', 'file',
|
||||
'xz', 'libtool', 'gettext-devel', 'file',
|
||||
];
|
||||
|
||||
public const TOOLS_ARCH = [
|
||||
|
||||
@@ -175,8 +175,21 @@ abstract class Package
|
||||
public function getArtifact(): ?Artifact
|
||||
{
|
||||
// find config
|
||||
$artifact_name = PackageConfig::get($this->name, 'artifact');
|
||||
return $artifact_name !== null ? ArtifactLoader::getArtifactInstance($artifact_name) : null;
|
||||
$artifact_field = PackageConfig::get($this->name, 'artifact');
|
||||
|
||||
if ($artifact_field === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (is_string($artifact_field)) {
|
||||
return ArtifactLoader::getArtifactInstance($artifact_field);
|
||||
}
|
||||
|
||||
if (is_array($artifact_field)) {
|
||||
return ArtifactLoader::getArtifactInstance($this->name);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,6 +13,8 @@ use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class Registry
|
||||
{
|
||||
private static ?string $current_registry_name = null;
|
||||
|
||||
/** @var string[] List of loaded registries */
|
||||
private static array $loaded_registries = [];
|
||||
|
||||
@@ -35,7 +37,7 @@ class Registry
|
||||
public static function getRegistryConfig(?string $registry_name = null): array
|
||||
{
|
||||
if ($registry_name === null && spc_mode(SPC_MODE_SOURCE)) {
|
||||
return self::$registry_configs['internal'];
|
||||
return self::$registry_configs['core'];
|
||||
}
|
||||
if ($registry_name !== null && isset(self::$registry_configs[$registry_name])) {
|
||||
return self::$registry_configs[$registry_name];
|
||||
@@ -83,6 +85,8 @@ class Registry
|
||||
|
||||
logger()->debug("Loading registry '{$registry_name}' from file: {$registry_file}");
|
||||
|
||||
self::$current_registry_name = $registry_name;
|
||||
|
||||
// Load composer autoload if specified (for external registries with their own dependencies)
|
||||
if (isset($data['autoload']) && is_string($data['autoload'])) {
|
||||
$autoload_path = FileSystem::fullpath($data['autoload'], dirname($registry_file));
|
||||
@@ -94,24 +98,6 @@ class Registry
|
||||
}
|
||||
}
|
||||
|
||||
// load doctor items from PSR-4 directories
|
||||
if (isset($data['doctor']['psr-4']) && is_assoc_array($data['doctor']['psr-4'])) {
|
||||
foreach ($data['doctor']['psr-4'] as $namespace => $path) {
|
||||
$path = FileSystem::fullpath($path, dirname($registry_file));
|
||||
DoctorLoader::loadFromPsr4Dir($path, $namespace, $auto_require);
|
||||
}
|
||||
}
|
||||
|
||||
// load doctor items from specific classes
|
||||
// Supports both array format ["ClassName"] and map format {"ClassName": "path/to/file.php"}
|
||||
if (isset($data['doctor']['classes']) && is_array($data['doctor']['classes'])) {
|
||||
foreach ($data['doctor']['classes'] as $key => $value) {
|
||||
[$class, $file] = self::parseClassEntry($key, $value);
|
||||
self::requireClassFile($class, $file, dirname($registry_file), $auto_require);
|
||||
DoctorLoader::loadFromClass($class);
|
||||
}
|
||||
}
|
||||
|
||||
// load package configs
|
||||
if (isset($data['package']['config']) && is_array($data['package']['config'])) {
|
||||
foreach ($data['package']['config'] as $path) {
|
||||
@@ -136,6 +122,24 @@ class Registry
|
||||
}
|
||||
}
|
||||
|
||||
// load doctor items from PSR-4 directories
|
||||
if (isset($data['doctor']['psr-4']) && is_assoc_array($data['doctor']['psr-4'])) {
|
||||
foreach ($data['doctor']['psr-4'] as $namespace => $path) {
|
||||
$path = FileSystem::fullpath($path, dirname($registry_file));
|
||||
DoctorLoader::loadFromPsr4Dir($path, $namespace, $auto_require);
|
||||
}
|
||||
}
|
||||
|
||||
// load doctor items from specific classes
|
||||
// Supports both array format ["ClassName"] and map format {"ClassName": "path/to/file.php"}
|
||||
if (isset($data['doctor']['classes']) && is_array($data['doctor']['classes'])) {
|
||||
foreach ($data['doctor']['classes'] as $key => $value) {
|
||||
[$class, $file] = self::parseClassEntry($key, $value);
|
||||
self::requireClassFile($class, $file, dirname($registry_file), $auto_require);
|
||||
DoctorLoader::loadFromClass($class);
|
||||
}
|
||||
}
|
||||
|
||||
// load packages from PSR-4 directories
|
||||
if (isset($data['package']['psr-4']) && is_assoc_array($data['package']['psr-4'])) {
|
||||
foreach ($data['package']['psr-4'] as $namespace => $path) {
|
||||
@@ -193,6 +197,7 @@ class Registry
|
||||
}
|
||||
ConsoleApplication::_addAdditionalCommands($instances);
|
||||
}
|
||||
self::$current_registry_name = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -300,6 +305,11 @@ class Registry
|
||||
return self::$loaded_artifact_configs;
|
||||
}
|
||||
|
||||
public static function getCurrentRegistryName(): ?string
|
||||
{
|
||||
return self::$current_registry_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a class entry from the classes array.
|
||||
* Supports two formats:
|
||||
|
||||
@@ -163,18 +163,26 @@ class LicenseDumper
|
||||
{
|
||||
$artifact_name = $artifact->getName();
|
||||
|
||||
// Try source directory first (if extracted)
|
||||
if ($artifact->isSourceExtracted()) {
|
||||
$source_dir = $artifact->getSourceDir();
|
||||
$full_path = "{$source_dir}/{$license_file_path}";
|
||||
// replace
|
||||
if (str_starts_with($license_file_path, '@/')) {
|
||||
$license_file_path = str_replace('@/', ROOT_DIR . '/src/globals/licenses/', $license_file_path);
|
||||
}
|
||||
|
||||
$source_dir = $artifact->getSourceDir();
|
||||
if (FileSystem::isRelativePath($license_file_path)) {
|
||||
$full_path = "{$source_dir}/{$license_file_path}";
|
||||
} else {
|
||||
$full_path = $license_file_path;
|
||||
}
|
||||
// Try source directory first (if extracted)
|
||||
if ($artifact->isSourceExtracted() || file_exists($full_path)) {
|
||||
logger()->debug("Checking license file: {$full_path}");
|
||||
if (file_exists($full_path)) {
|
||||
logger()->info("Reading license from source: {$full_path}");
|
||||
return file_get_contents($full_path);
|
||||
}
|
||||
} else {
|
||||
logger()->debug("Artifact source not extracted: {$artifact_name}");
|
||||
logger()->warning("Artifact source not extracted: {$artifact_name}");
|
||||
}
|
||||
|
||||
// Fallback: try SOURCE_PATH directly
|
||||
|
||||
Reference in New Issue
Block a user