mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-03-17 20:34:51 +08:00
Add doctor cache check and version management to ensure environment validation
This commit is contained in:
parent
7623b9e673
commit
c218aef947
3
.gitignore
vendored
3
.gitignore
vendored
@ -33,6 +33,9 @@ packlib_files.txt
|
|||||||
.php-cs-fixer.cache
|
.php-cs-fixer.cache
|
||||||
.phpunit.result.cache
|
.phpunit.result.cache
|
||||||
|
|
||||||
|
# doctor cache fallback (when ~/.cache/spc/ is not writable)
|
||||||
|
.spc-doctor.lock
|
||||||
|
|
||||||
# exclude self-runtime
|
# exclude self-runtime
|
||||||
/bin/*
|
/bin/*
|
||||||
!/bin/spc*
|
!/bin/spc*
|
||||||
|
|||||||
@ -7,6 +7,7 @@ namespace Package\Target;
|
|||||||
use Package\Target\php\frankenphp;
|
use Package\Target\php\frankenphp;
|
||||||
use Package\Target\php\unix;
|
use Package\Target\php\unix;
|
||||||
use Package\Target\php\windows;
|
use Package\Target\php\windows;
|
||||||
|
use StaticPHP\Artifact\ArtifactCache;
|
||||||
use StaticPHP\Attribute\Package\BeforeStage;
|
use StaticPHP\Attribute\Package\BeforeStage;
|
||||||
use StaticPHP\Attribute\Package\Info;
|
use StaticPHP\Attribute\Package\Info;
|
||||||
use StaticPHP\Attribute\Package\InitPackage;
|
use StaticPHP\Attribute\Package\InitPackage;
|
||||||
@ -104,6 +105,24 @@ class php extends TargetPackage
|
|||||||
throw new WrongUsageException('PHP version file format is malformed, please remove "./source/php-src" dir and download/extract again');
|
throw new WrongUsageException('PHP version file format is malformed, please remove "./source/php-src" dir and download/extract again');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get PHP version from source archive filename
|
||||||
|
*
|
||||||
|
* @return null|string PHP version (e.g., "8.4.0")
|
||||||
|
*/
|
||||||
|
public static function getPHPVersionFromArchive(bool $return_null_if_failed = false): ?string
|
||||||
|
{
|
||||||
|
$archives = ApplicationContext::get(ArtifactCache::class)->getSourceInfo('php-src');
|
||||||
|
$filename = $archives['filename'] ?? '';
|
||||||
|
if (!preg_match('/php-(\d+\.\d+\.\d+(?:RC\d+|alpha\d+|beta\d+)?)\.tar\.(?:gz|bz2|xz)/', $filename, $match)) {
|
||||||
|
if ($return_null_if_failed) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
throw new WrongUsageException('PHP source archive filename format is malformed (got: ' . $filename . ')');
|
||||||
|
}
|
||||||
|
return $match[1];
|
||||||
|
}
|
||||||
|
|
||||||
#[InitPackage]
|
#[InitPackage]
|
||||||
public function init(TargetPackage $package): void
|
public function init(TargetPackage $package): void
|
||||||
{
|
{
|
||||||
@ -255,6 +274,7 @@ class php extends TargetPackage
|
|||||||
'Build Target' => getenv('SPC_TARGET') ?: '',
|
'Build Target' => getenv('SPC_TARGET') ?: '',
|
||||||
'Build Toolchain' => ToolchainManager::getToolchainClass(),
|
'Build Toolchain' => ToolchainManager::getToolchainClass(),
|
||||||
'Build SAPI' => implode(', ', $sapis),
|
'Build SAPI' => implode(', ', $sapis),
|
||||||
|
'PHP Version' => self::getPHPVersion(return_null_if_failed: true) ?? self::getPHPVersionFromArchive(return_null_if_failed: true) ?? 'Unknown',
|
||||||
'Static Extensions (' . count($static_extensions) . ')' => implode(',', array_map(fn ($x) => substr($x->getName(), 4), $static_extensions)),
|
'Static Extensions (' . count($static_extensions) . ')' => implode(',', array_map(fn ($x) => substr($x->getName(), 4), $static_extensions)),
|
||||||
'Shared Extensions (' . count($shared_extensions) . ')' => implode(',', $shared_extensions),
|
'Shared Extensions (' . count($shared_extensions) . ')' => implode(',', $shared_extensions),
|
||||||
'Install Packages (' . count($install_packages) . ')' => implode(',', array_map(fn ($x) => $x->getName(), $install_packages)),
|
'Install Packages (' . count($install_packages) . ')' => implode(',', array_map(fn ($x) => $x->getName(), $install_packages)),
|
||||||
|
|||||||
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace StaticPHP\Command;
|
namespace StaticPHP\Command;
|
||||||
|
|
||||||
use StaticPHP\DI\ApplicationContext;
|
use StaticPHP\DI\ApplicationContext;
|
||||||
|
use StaticPHP\Doctor\Doctor;
|
||||||
use StaticPHP\Exception\ExceptionHandler;
|
use StaticPHP\Exception\ExceptionHandler;
|
||||||
use StaticPHP\Exception\SPCException;
|
use StaticPHP\Exception\SPCException;
|
||||||
use Symfony\Component\Console\Command\Command;
|
use Symfony\Component\Console\Command\Command;
|
||||||
@ -118,6 +119,21 @@ abstract class BaseCommand extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Warn the user if doctor has not been run (or is outdated).
|
||||||
|
* Set SPC_SKIP_DOCTOR_CHECK=1 to suppress.
|
||||||
|
*/
|
||||||
|
protected function checkDoctorCache(): void
|
||||||
|
{
|
||||||
|
if (getenv('SPC_SKIP_DOCTOR_CHECK') || Doctor::isHealthy()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->output->writeln('');
|
||||||
|
$this->output->writeln('<comment>[WARNING] Please run `spc doctor` first to verify your build environment.</comment>');
|
||||||
|
$this->output->writeln('');
|
||||||
|
sleep(2);
|
||||||
|
}
|
||||||
|
|
||||||
protected function getOption(string $name): mixed
|
protected function getOption(string $name): mixed
|
||||||
{
|
{
|
||||||
return $this->input->getOption($name);
|
return $this->input->getOption($name);
|
||||||
|
|||||||
@ -44,6 +44,8 @@ class BuildLibsCommand extends BaseCommand
|
|||||||
|
|
||||||
public function handle(): int
|
public function handle(): int
|
||||||
{
|
{
|
||||||
|
$this->checkDoctorCache();
|
||||||
|
|
||||||
$libs = parse_comma_list($this->input->getArgument('libraries'));
|
$libs = parse_comma_list($this->input->getArgument('libraries'));
|
||||||
|
|
||||||
$installer = new PackageInstaller($this->input->getOptions());
|
$installer = new PackageInstaller($this->input->getOptions());
|
||||||
|
|||||||
@ -37,6 +37,8 @@ class BuildTargetCommand extends BaseCommand
|
|||||||
|
|
||||||
public function handle(): int
|
public function handle(): int
|
||||||
{
|
{
|
||||||
|
$this->checkDoctorCache();
|
||||||
|
|
||||||
// resolve legacy options to new options
|
// resolve legacy options to new options
|
||||||
V2CompatLayer::convertOptions($this->input);
|
V2CompatLayer::convertOptions($this->input);
|
||||||
|
|
||||||
|
|||||||
@ -26,6 +26,7 @@ class DoctorCommand extends BaseCommand
|
|||||||
};
|
};
|
||||||
$doctor = new Doctor($this->output, $fix_policy);
|
$doctor = new Doctor($this->output, $fix_policy);
|
||||||
if ($doctor->checkAll()) {
|
if ($doctor->checkAll()) {
|
||||||
|
Doctor::markPassed();
|
||||||
$this->output->writeln('<info>Doctor check complete !</info>');
|
$this->output->writeln('<info>Doctor check complete !</info>');
|
||||||
return static::SUCCESS;
|
return static::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,6 +56,8 @@ class DownloadCommand extends BaseCommand
|
|||||||
return $this->handleClean();
|
return $this->handleClean();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->checkDoctorCache();
|
||||||
|
|
||||||
$downloader = new ArtifactDownloader(DownloaderOptions::extractFromConsoleOptions($this->input->getOptions()));
|
$downloader = new ArtifactDownloader(DownloaderOptions::extractFromConsoleOptions($this->input->getOptions()));
|
||||||
|
|
||||||
// arguments
|
// arguments
|
||||||
|
|||||||
@ -9,6 +9,7 @@ use StaticPHP\DI\ApplicationContext;
|
|||||||
use StaticPHP\Exception\SPCException;
|
use StaticPHP\Exception\SPCException;
|
||||||
use StaticPHP\Registry\DoctorLoader;
|
use StaticPHP\Registry\DoctorLoader;
|
||||||
use StaticPHP\Runtime\Shell\Shell;
|
use StaticPHP\Runtime\Shell\Shell;
|
||||||
|
use StaticPHP\Runtime\SystemTarget;
|
||||||
use StaticPHP\Util\InteractiveTerm;
|
use StaticPHP\Util\InteractiveTerm;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use ZM\Logger\ConsoleColor;
|
use ZM\Logger\ConsoleColor;
|
||||||
@ -25,6 +26,29 @@ readonly class Doctor
|
|||||||
logger()->debug("Loaded doctor check items:\n\t" . implode("\n\t", $names));
|
logger()->debug("Loaded doctor check items:\n\t" . implode("\n\t", $names));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if doctor was previously passed with the current SPC version.
|
||||||
|
*/
|
||||||
|
public static function isHealthy(): bool
|
||||||
|
{
|
||||||
|
$lock = self::getLockPath();
|
||||||
|
return file_exists($lock) && trim((string) @file_get_contents($lock)) === \StaticPHP\ConsoleApplication::VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write current SPC version to the lock file, marking doctor as passed.
|
||||||
|
*/
|
||||||
|
public static function markPassed(): void
|
||||||
|
{
|
||||||
|
$primary = self::getLockPath();
|
||||||
|
if (!is_dir(dirname($primary))) {
|
||||||
|
@mkdir(dirname($primary), 0755, true);
|
||||||
|
}
|
||||||
|
if (@file_put_contents($primary, \StaticPHP\ConsoleApplication::VERSION) === false) {
|
||||||
|
@file_put_contents((getcwd() ?: '.') . DIRECTORY_SEPARATOR . '.spc-doctor.lock', \StaticPHP\ConsoleApplication::VERSION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check all valid check items.
|
* Check all valid check items.
|
||||||
* @return bool true if all checks passed, false otherwise
|
* @return bool true if all checks passed, false otherwise
|
||||||
@ -119,6 +143,30 @@ readonly class Doctor
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function getLockPath(): string
|
||||||
|
{
|
||||||
|
if (SystemTarget::getTargetOS() === 'Windows') {
|
||||||
|
$trial_ls = [
|
||||||
|
getenv('LOCALAPPDATA') ?: ((getenv('USERPROFILE') ?: 'C:\Users\Default') . '\AppData\Local') . '\.spc-doctor.lock',
|
||||||
|
sys_get_temp_dir() . '\.spc-doctor.lock',
|
||||||
|
WORKING_DIR . '\.spc-doctor.lock',
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$trial_ls = [
|
||||||
|
getenv('XDG_CACHE_HOME') ?: ((getenv('HOME') ?: '/tmp') . '/.cache') . '/.spc-doctor.lock',
|
||||||
|
sys_get_temp_dir() . '/.spc-doctor.lock',
|
||||||
|
WORKING_DIR . '/.spc-doctor.lock',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
foreach ($trial_ls as $path) {
|
||||||
|
if (is_writable(dirname($path))) {
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// fallback to current directory
|
||||||
|
return WORKING_DIR . DIRECTORY_SEPARATOR . '.spc-doctor.lock';
|
||||||
|
}
|
||||||
|
|
||||||
private function emitFix(string $fix_item, array $fix_item_params = []): bool
|
private function emitFix(string $fix_item, array $fix_item_params = []): bool
|
||||||
{
|
{
|
||||||
keyboard_interrupt_register(function () {
|
keyboard_interrupt_register(function () {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user