Add HostedPackageBin downloader and enhance artifact handling

This commit is contained in:
crazywhalecc 2025-12-05 14:39:05 +08:00 committed by Jerry Ma
parent 52553fb5ed
commit 7fa6fd08d4
6 changed files with 111 additions and 14 deletions

View File

@ -12,6 +12,7 @@ use StaticPHP\Artifact\Downloader\Type\FileList;
use StaticPHP\Artifact\Downloader\Type\Git;
use StaticPHP\Artifact\Downloader\Type\GitHubRelease;
use StaticPHP\Artifact\Downloader\Type\GitHubTarball;
use StaticPHP\Artifact\Downloader\Type\HostedPackageBin;
use StaticPHP\Artifact\Downloader\Type\LocalDir;
use StaticPHP\Artifact\Downloader\Type\PhpRelease;
use StaticPHP\Artifact\Downloader\Type\PIE;
@ -35,6 +36,19 @@ use ZM\Logger\ConsoleColor;
*/
class ArtifactDownloader
{
public const array DOWNLOADERS = [
'bitbuckettag' => BitBucketTag::class,
'filelist' => FileList::class,
'git' => Git::class,
'ghrel' => GitHubRelease::class,
'ghtar', 'ghtagtar' => GitHubTarball::class,
'local' => LocalDir::class,
'pie' => PIE::class,
'url' => Url::class,
'php-release' => PhpRelease::class,
'hosted' => HostedPackageBin::class,
];
/** @var array<string, Artifact> Artifact objects */
protected array $artifacts = [];
@ -355,18 +369,7 @@ class ArtifactDownloader
foreach ($queue as $item) {
try {
$instance = null;
$call = match ($item['config']['type']) {
'bitbuckettag' => BitBucketTag::class,
'filelist' => FileList::class,
'git' => Git::class,
'ghrel' => GitHubRelease::class,
'ghtar', 'ghtagtar' => GitHubTarball::class,
'local' => LocalDir::class,
'pie' => PIE::class,
'url' => Url::class,
'php-release' => PhpRelease::class,
default => null,
};
$call = self::DOWNLOADERS[$item['config']['type']] ?? null;
$type_display_name = match (true) {
$item['lock'] === 'source' && ($callback = $artifact->getCustomSourceCallback()) !== null => 'user defined source downloader',
$item['lock'] === 'binary' && ($callback = $artifact->getCustomBinaryCallback()) !== null => 'user defined binary downloader',

View File

@ -21,6 +21,26 @@ class GitHubRelease implements DownloadTypeInterface, ValidatorInterface
private ?string $version = null;
public function getGitHubReleases(string $name, string $repo, bool $prefer_stable = true): array
{
logger()->debug("Fetching {$name} GitHub releases from {$repo}");
$url = str_replace('{repo}', $repo, self::API_URL);
$headers = $this->getGitHubTokenHeaders();
$data2 = default_shell()->executeCurl($url, headers: $headers);
$data = json_decode($data2 ?: '', true);
if (!is_array($data)) {
throw new DownloaderException("Failed to get GitHub release API info for {$repo} from {$url}");
}
$releases = [];
foreach ($data as $release) {
if ($prefer_stable && $release['prerelease'] === true) {
continue;
}
$releases[] = $release;
}
return $releases;
}
/**
* Get the latest GitHub release assets for a given repository.
* match_asset is provided, only return the asset that matches the regex.

View File

@ -7,6 +7,11 @@ namespace StaticPHP\Artifact\Downloader\Type;
trait GitHubTokenSetupTrait
{
public function getGitHubTokenHeaders(): array
{
return self::getGitHubTokenHeadersStatic();
}
public static function getGitHubTokenHeadersStatic(): array
{
// GITHUB_TOKEN support
if (($token = getenv('GITHUB_TOKEN')) !== false && ($user = getenv('GITHUB_USER')) !== false) {

View File

@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace StaticPHP\Artifact\Downloader\Type;
use StaticPHP\Artifact\ArtifactDownloader;
use StaticPHP\Artifact\Downloader\DownloadResult;
use StaticPHP\Exception\DownloaderException;
use StaticPHP\Runtime\SystemTarget;
class HostedPackageBin implements DownloadTypeInterface
{
use GitHubTokenSetupTrait;
public const string BASE_REPO = 'static-php/package-bin';
public const array ASSET_MATCHES = [
'linux' => '{name}-{arch}-{os}-{libc}-{libcver}.txz',
'darwin' => '{name}-{arch}-{os}.txz',
'windows' => '{name}-{arch}-{os}.tgz',
];
private static array $release_info = [];
public static function getReleaseInfo(): array
{
if (empty(self::$release_info)) {
$rel = (new GitHubRelease())->getGitHubReleases('hosted', self::BASE_REPO);
if (empty($rel)) {
throw new DownloaderException('No releases found for hosted package-bin');
}
self::$release_info = $rel[0];
}
return self::$release_info;
}
public function download(string $name, array $config, ArtifactDownloader $downloader): DownloadResult
{
$info = self::getReleaseInfo();
$replace = [
'{name}' => $name,
'{arch}' => SystemTarget::getTargetArch(),
'{os}' => strtolower(SystemTarget::getTargetOS()),
'{libc}' => SystemTarget::getLibc() ?? 'default',
'{libcver}' => SystemTarget::getLibcVersion() ?? 'default',
];
$find_str = str_replace(array_keys($replace), array_values($replace), self::ASSET_MATCHES[strtolower(SystemTarget::getTargetOS())]);
foreach ($info['assets'] as $asset) {
if ($asset['name'] === $find_str) {
$download_url = $asset['browser_download_url'];
$filename = $asset['name'];
$version = ltrim($info['tag_name'], 'v');
logger()->debug("Downloading hosted package-bin {$name} version {$version} from GitHub: {$download_url}");
$path = DOWNLOAD_PATH . DIRECTORY_SEPARATOR . $filename;
$headers = $this->getGitHubTokenHeaders();
default_shell()->executeCurlDownload($download_url, $path, headers: $headers, retries: $downloader->getRetry());
return DownloadResult::archive($filename, $config, extract: $config['extract'] ?? null, version: $version);
}
}
throw new DownloaderException("No matching asset found for hosted package-bin {$name} with criteria: {$find_str}");
}
}

View File

@ -33,7 +33,7 @@ class SPCConfigCommand extends BaseCommand
{
// transform string to array
$libraries = parse_comma_list($this->getOption('with-libs'));
$libraries = array_merge($libraries, $this->getOption('with-packages'));
$libraries = array_merge($libraries, parse_comma_list($this->getOption('with-packages')));
// transform string to array
$extensions = $this->getArgument('extensions') ? parse_extension_list($this->getArgument('extensions')) : [];
$include_suggests = $this->getOption('with-suggests') ?: $this->getOption('with-suggested-libs') || $this->getOption('with-suggested-exts');

View File

@ -137,8 +137,14 @@ class ConfigValidator
];
continue;
}
// TODO: expand hosted to static-php hosted download urls
if ($v === 'hosted') {
$data[$name][$k] = [
'linux-x86_64' => ['type' => 'hosted'],
'linux-aarch64' => ['type' => 'hosted'],
'windows-x86_64' => ['type' => 'hosted'],
'macos-x86_64' => ['type' => 'hosted'],
'macos-aarch64' => ['type' => 'hosted'],
];
continue;
}
if (is_assoc_array($v)) {