Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 26 additions & 14 deletions src/Package/Artifact/go_xcaddy.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use StaticPHP\Artifact\Downloader\DownloadResult;
use StaticPHP\Attribute\Artifact\AfterBinaryExtract;
use StaticPHP\Attribute\Artifact\CustomBinary;
use StaticPHP\Exception\ValidationException;
use StaticPHP\Exception\DownloaderException;
use StaticPHP\Runtime\SystemTarget;
use StaticPHP\Util\System\LinuxUtil;

Expand All @@ -28,29 +28,41 @@ public function downBinary(ArtifactDownloader $downloader): DownloadResult
$arch = match (explode('-', $name)[1]) {
'x86_64' => 'amd64',
'aarch64' => 'arm64',
default => throw new ValidationException('Unsupported architecture: ' . $name),
default => throw new DownloaderException('Unsupported architecture: ' . $name),
};
$os = match (explode('-', $name)[0]) {
'linux' => 'linux',
'macos' => 'darwin',
default => throw new ValidationException('Unsupported OS: ' . $name),
default => throw new DownloaderException('Unsupported OS: ' . $name),
};
$hash = match ("{$os}-{$arch}") {
'linux-amd64' => '2852af0cb20a13139b3448992e69b868e50ed0f8a1e5940ee1de9e19a123b613',
'linux-arm64' => '05de75d6994a2783699815ee553bd5a9327d8b79991de36e38b66862782f54ae',
'darwin-amd64' => '5bd60e823037062c2307c71e8111809865116714d6f6b410597cf5075dfd80ef',
'darwin-arm64' => '544932844156d8172f7a28f77f2ac9c15a23046698b6243f633b0a0b00c0749c',
};
$go_version = '1.25.0';
$url = "https://go.dev/dl/go{$go_version}.{$os}-{$arch}.tar.gz";
$path = DOWNLOAD_PATH . DIRECTORY_SEPARATOR . "go{$go_version}.{$os}-{$arch}.tar.gz";

// get version and hash
[$version] = explode("\n", default_shell()->executeCurl('https://go.dev/VERSION?m=text') ?: '');
if ($version === '') {
throw new DownloaderException('Failed to get latest Go version from https://go.dev/VERSION?m=text');
}
$page = default_shell()->executeCurl('https://go.dev/dl/');
if ($page === '' || $page === false) {
throw new DownloaderException('Failed to get Go download page from https://go.dev/dl/');
}

$version_regex = str_replace('.', '\.', $version);
$pattern = "/href=\"\\/dl\\/{$version_regex}\\.{$os}-{$arch}\\.tar\\.gz\">.*?<tt>([a-f0-9]{64})<\\/tt>/s";
if (preg_match($pattern, $page, $matches)) {
$hash = $matches[1];
} else {
throw new DownloaderException("Failed to find download hash for Go {$version} {$os}-{$arch}");
}

$url = "https://go.dev/dl/{$version}.{$os}-{$arch}.tar.gz";
$path = DOWNLOAD_PATH . DIRECTORY_SEPARATOR . "{$version}.{$os}-{$arch}.tar.gz";
default_shell()->executeCurlDownload($url, $path, retries: $downloader->getRetry());
// verify hash
$file_hash = hash_file('sha256', $path);
if ($file_hash !== $hash) {
throw new ValidationException("Hash mismatch for downloaded go-xcaddy binary. Expected {$hash}, got {$file_hash}");
throw new DownloaderException("Hash mismatch for downloaded go-xcaddy binary. Expected {$hash}, got {$file_hash}");
}
return DownloadResult::archive(basename($path), ['url' => $url, 'version' => $go_version], extract: "{$pkgroot}/go-xcaddy", verified: true, version: $go_version);
return DownloadResult::archive(basename($path), ['url' => $url, 'version' => $version], extract: "{$pkgroot}/go-xcaddy", verified: true, version: $version);
}

#[AfterBinaryExtract('go-xcaddy', [
Expand Down
29 changes: 17 additions & 12 deletions src/StaticPHP/Artifact/ArtifactDownloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
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;
Expand All @@ -35,6 +36,21 @@
*/
class ArtifactDownloader
{
/** @var array<string, class-string<DownloadTypeInterface>> */
public const array DOWNLOADERS = [
'bitbuckettag' => BitBucketTag::class,
'filelist' => FileList::class,
'git' => Git::class,
'ghrel' => GitHubRelease::class,
'ghtar' => GitHubTarball::class,
'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 = [];

Expand Down Expand Up @@ -355,18 +371,7 @@ private function downloadWithType(Artifact $artifact, int $current, int $total,
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',
Expand Down
20 changes: 20 additions & 0 deletions src/StaticPHP/Artifact/Downloader/Type/GitHubRelease.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
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) {
Expand Down
63 changes: 63 additions & 0 deletions src/StaticPHP/Artifact/Downloader/Type/HostedPackageBin.php
Original file line number Diff line number Diff line change
@@ -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}: {$find_str}");
}
}
2 changes: 2 additions & 0 deletions src/StaticPHP/Command/DownloadCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public function configure(): void
$this->addOption('for-libs', 'l', InputOption::VALUE_REQUIRED, 'Fetch by libraries, e.g "libcares,openssl,onig"');
$this->addOption('without-suggests', null, null, 'Do not fetch suggested sources when using --for-extensions');

$this->addOption('without-suggestions', null, null, '(deprecated) Do not fetch suggested sources when using --for-extensions');

// download command specific options
$this->addOption('clean', null, null, 'Clean old download cache and source before fetch');
$this->addOption('for-packages', null, InputOption::VALUE_REQUIRED, 'Fetch by packages, e.g "php,libssl,libcurl"');
Expand Down
2 changes: 1 addition & 1 deletion src/StaticPHP/Command/SPCConfigCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public function handle(): int
{
// 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');
Expand Down
8 changes: 7 additions & 1 deletion src/StaticPHP/Config/ConfigValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,14 @@ public static function validateAndLintArtifacts(string $config_file_name, mixed
];
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)) {
Expand Down
11 changes: 0 additions & 11 deletions src/StaticPHP/Util/FileSystem.php
Original file line number Diff line number Diff line change
Expand Up @@ -370,17 +370,6 @@ public static function resetDir(string $dir_name): void
self::createDir($dir_name);
}

/**
* Add source extraction hook
*
* @param string $name Source name
* @param callable $callback Callback function
*/
public static function addSourceExtractHook(string $name, callable $callback): void
{
self::$_extract_hook[$name][] = $callback;
}

/**
* Check if path is relative
*
Expand Down
29 changes: 13 additions & 16 deletions src/StaticPHP/Util/SPCConfigUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

use StaticPHP\Config\PackageConfig;
use StaticPHP\Exception\WrongUsageException;
use StaticPHP\Package\LibraryPackage;
use StaticPHP\Package\PhpExtensionPackage;
use StaticPHP\Runtime\SystemTarget;

class SPCConfigUtil
Expand Down Expand Up @@ -99,42 +101,37 @@ public function config(array $packages = [], bool $include_suggests = false): ar
* [Helper function]
* Get configuration for a specific extension(s) dependencies.
*
* @param Extension|Extension[] $extension Extension instance or list
* @param bool $include_suggest_ext Whether to include suggested extensions
* @param bool $include_suggest_lib Whether to include suggested libraries
* @param array|PhpExtensionPackage $extension_packages Extension instance or list
* @return array{
* cflags: string,
* ldflags: string,
* libs: string
* }
*/
public function getExtensionConfig(array|Extension $extension, bool $include_suggest_ext = false, bool $include_suggest_lib = false): array
public function getExtensionConfig(array|PhpExtensionPackage $extension_packages, bool $include_suggests = false): array
{
if (!is_array($extension)) {
$extension = [$extension];
if (!is_array($extension_packages)) {
$extension_packages = [$extension_packages];
}
$libs = array_map(fn ($y) => $y->getName(), array_merge(...array_map(fn ($x) => $x->getLibraryDependencies(true), $extension)));
return $this->config(
extensions: array_map(fn ($x) => $x->getName(), $extension),
libraries: $libs,
include_suggest_ext: $include_suggest_ext ?: $this->builder?->getOption('with-suggested-exts') ?? false,
include_suggest_lib: $include_suggest_lib ?: $this->builder?->getOption('with-suggested-libs') ?? false,
packages: array_map(fn ($y) => $y->getName(), $extension_packages),
include_suggests: $include_suggests,
);
}

/**
* [Helper function]
* Get configuration for a specific library(s) dependencies.
*
* @param LibraryBase|LibraryBase[] $lib Library instance or list
* @param bool $include_suggest_lib Whether to include suggested libraries
* @param array|LibraryPackage $lib Library instance or list
* @param bool $include_suggests Whether to include suggested libraries
* @return array{
* cflags: string,
* ldflags: string,
* libs: string
* }
*/
public function getLibraryConfig(array|LibraryBase $lib, bool $include_suggest_lib = false): array
public function getLibraryConfig(array|LibraryPackage $lib, bool $include_suggests = false): array
{
if (!is_array($lib)) {
$lib = [$lib];
Expand All @@ -144,8 +141,8 @@ public function getLibraryConfig(array|LibraryBase $lib, bool $include_suggest_l
$save_libs_only_deps = $this->libs_only_deps;
$this->libs_only_deps = true;
$ret = $this->config(
libraries: array_map(fn ($x) => $x->getName(), $lib),
include_suggest_lib: $include_suggest_lib ?: $this->builder?->getOption('with-suggested-libs') ?? false,
packages: array_map(fn ($y) => $y->getName(), $lib),
include_suggests: $include_suggests,
);
$this->no_php = $save_no_php;
$this->libs_only_deps = $save_libs_only_deps;
Expand Down