From 40e36982d342a7abcaba4277f8ebee5ee9a52c02 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sat, 28 Feb 2026 13:55:52 +0800 Subject: [PATCH] Add custom binary check-update support for artifacts --- src/Package/Artifact/go_xcaddy.php | 21 ++++++++++++++ src/Package/Artifact/zig.php | 29 +++++++++++++++++++ src/StaticPHP/Artifact/Artifact.php | 21 ++++++++++++++ src/StaticPHP/Artifact/ArtifactDownloader.php | 9 ++++-- .../Artifact/CustomBinaryCheckUpdate.php | 11 +++++++ src/StaticPHP/Registry/ArtifactLoader.php | 22 ++++++++++++++ 6 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 src/StaticPHP/Attribute/Artifact/CustomBinaryCheckUpdate.php diff --git a/src/Package/Artifact/go_xcaddy.php b/src/Package/Artifact/go_xcaddy.php index 5fa7327e..056a10e3 100644 --- a/src/Package/Artifact/go_xcaddy.php +++ b/src/Package/Artifact/go_xcaddy.php @@ -6,8 +6,10 @@ namespace Package\Artifact; use StaticPHP\Artifact\ArtifactDownloader; use StaticPHP\Artifact\Downloader\DownloadResult; +use StaticPHP\Artifact\Downloader\Type\CheckUpdateResult; use StaticPHP\Attribute\Artifact\AfterBinaryExtract; use StaticPHP\Attribute\Artifact\CustomBinary; +use StaticPHP\Attribute\Artifact\CustomBinaryCheckUpdate; use StaticPHP\Exception\DownloaderException; use StaticPHP\Runtime\SystemTarget; use StaticPHP\Util\GlobalEnvManager; @@ -65,6 +67,25 @@ class go_xcaddy return DownloadResult::archive(basename($path), ['url' => $url, 'version' => $version], extract: "{$pkgroot}/go-xcaddy", verified: true, version: $version); } + #[CustomBinaryCheckUpdate('go-xcaddy', [ + 'linux-x86_64', + 'linux-aarch64', + 'macos-x86_64', + 'macos-aarch64', + ])] + public function checkUpdateBinary(?string $old_version, ArtifactDownloader $downloader): CheckUpdateResult + { + [$version] = explode("\n", default_shell()->executeCurl('https://go.dev/VERSION?m=text') ?: ''); + if ($version === '') { + throw new \RuntimeException('Failed to get latest Go version from https://go.dev/VERSION?m=text'); + } + return new CheckUpdateResult( + old: $old_version, + new: $version, + needUpdate: $old_version === null || $version !== $old_version, + ); + } + #[AfterBinaryExtract('go-xcaddy', [ 'linux-x86_64', 'linux-aarch64', diff --git a/src/Package/Artifact/zig.php b/src/Package/Artifact/zig.php index 9a143063..0d334e5f 100644 --- a/src/Package/Artifact/zig.php +++ b/src/Package/Artifact/zig.php @@ -6,8 +6,10 @@ namespace Package\Artifact; use StaticPHP\Artifact\ArtifactDownloader; use StaticPHP\Artifact\Downloader\DownloadResult; +use StaticPHP\Artifact\Downloader\Type\CheckUpdateResult; use StaticPHP\Attribute\Artifact\AfterBinaryExtract; use StaticPHP\Attribute\Artifact\CustomBinary; +use StaticPHP\Attribute\Artifact\CustomBinaryCheckUpdate; use StaticPHP\Exception\DownloaderException; use StaticPHP\Runtime\SystemTarget; @@ -59,6 +61,33 @@ class zig return DownloadResult::archive(basename($path), ['url' => $url, 'version' => $latest_version], extract: PKG_ROOT_PATH . '/zig', verified: true, version: $latest_version); } + #[CustomBinaryCheckUpdate('zig', [ + 'linux-x86_64', + 'linux-aarch64', + 'macos-x86_64', + 'macos-aarch64', + ])] + public function checkUpdateBinary(?string $old_version, ArtifactDownloader $downloader): CheckUpdateResult + { + $index_json = default_shell()->executeCurl('https://ziglang.org/download/index.json', retries: $downloader->getRetry()); + $index_json = json_decode($index_json ?: '', true); + $latest_version = null; + foreach ($index_json as $version => $data) { + if ($version !== 'master') { + $latest_version = $version; + break; + } + } + if (!$latest_version) { + throw new DownloaderException('Could not determine latest Zig version'); + } + return new CheckUpdateResult( + old: $old_version, + new: $latest_version, + needUpdate: $old_version === null || version_compare($latest_version, $old_version, '>'), + ); + } + #[AfterBinaryExtract('zig', [ 'linux-x86_64', 'linux-aarch64', diff --git a/src/StaticPHP/Artifact/Artifact.php b/src/StaticPHP/Artifact/Artifact.php index 6dc35ad5..8bdd86a8 100644 --- a/src/StaticPHP/Artifact/Artifact.php +++ b/src/StaticPHP/Artifact/Artifact.php @@ -30,6 +30,9 @@ class Artifact /** @var array Bind custom binary fetcher callbacks */ protected mixed $custom_binary_callbacks = []; + /** @var array Bind custom binary check-update callbacks */ + protected array $custom_binary_check_update_callbacks = []; + /** @var null|callable Bind custom source extract callback (completely takes over extraction) */ protected mixed $source_extract_callback = null; @@ -433,6 +436,24 @@ class Artifact $this->custom_binary_callbacks[$target_os] = $callback; } + /** + * Set custom binary check-update callback for a specific target OS. + * + * @param string $target_os Target OS platform string (e.g. linux-x86_64) + * @param callable $callback Custom binary check-update callback + */ + public function setCustomBinaryCheckUpdateCallback(string $target_os, callable $callback): void + { + ConfigValidator::validatePlatformString($target_os); + $this->custom_binary_check_update_callbacks[$target_os] = $callback; + } + + public function getCustomBinaryCheckUpdateCallback(): ?callable + { + $current_platform = SystemTarget::getCurrentPlatformString(); + return $this->custom_binary_check_update_callbacks[$current_platform] ?? null; + } + // ==================== Extraction Callbacks ==================== /** diff --git a/src/StaticPHP/Artifact/ArtifactDownloader.php b/src/StaticPHP/Artifact/ArtifactDownloader.php index b2773c80..7740d27e 100644 --- a/src/StaticPHP/Artifact/ArtifactDownloader.php +++ b/src/StaticPHP/Artifact/ArtifactDownloader.php @@ -358,8 +358,13 @@ class ArtifactDownloader /** @var CheckUpdateInterface $downloader */ $downloader = new $cls(); return $downloader->checkUpdate($artifact_name, $info['config'], $info['version'], $this); - } - throw new WrongUsageException("Artifact '{$artifact_name}' downloader does not support update checking, exit."); + } // custom binary: delegate to registered check-update callback + if (($callback = $artifact->getCustomBinaryCheckUpdateCallback()) !== null) { + return ApplicationContext::invoke($callback, [ + ArtifactDownloader::class => $this, + 'old_version' => $info['version'], + ]); + } throw new WrongUsageException("Artifact '{$artifact_name}' downloader does not support update checking, exit."); } public function getRetry(): int diff --git a/src/StaticPHP/Attribute/Artifact/CustomBinaryCheckUpdate.php b/src/StaticPHP/Attribute/Artifact/CustomBinaryCheckUpdate.php new file mode 100644 index 00000000..aa59af1a --- /dev/null +++ b/src/StaticPHP/Attribute/Artifact/CustomBinaryCheckUpdate.php @@ -0,0 +1,11 @@ +getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { self::processCustomSourceAttribute($ref, $method, $class_instance); self::processCustomBinaryAttribute($ref, $method, $class_instance); + self::processCustomBinaryCheckUpdateAttribute($ref, $method, $class_instance); self::processSourceExtractAttribute($ref, $method, $class_instance); self::processBinaryExtractAttribute($ref, $method, $class_instance); self::processAfterSourceExtractAttribute($ref, $method, $class_instance); @@ -118,6 +120,26 @@ class ArtifactLoader } } + /** + * Process #[CustomBinaryCheckUpdate] attribute. + */ + private static function processCustomBinaryCheckUpdateAttribute(\ReflectionClass $ref, \ReflectionMethod $method, object $class_instance): void + { + $attributes = $method->getAttributes(CustomBinaryCheckUpdate::class); + foreach ($attributes as $attribute) { + /** @var CustomBinaryCheckUpdate $instance */ + $instance = $attribute->newInstance(); + $artifact_name = $instance->artifact_name; + if (isset(self::$artifacts[$artifact_name])) { + foreach ($instance->support_os as $os) { + self::$artifacts[$artifact_name]->setCustomBinaryCheckUpdateCallback($os, [$class_instance, $method->getName()]); + } + } else { + throw new ValidationException("Artifact '{$artifact_name}' not found for #[CustomBinaryCheckUpdate] on '{$ref->getName()}::{$method->getName()}'"); + } + } + } + /** * Process #[SourceExtract] attribute. * This attribute allows completely taking over the source extraction process.