Add alt download for Downloader (#706)

* Add alt download for Downloader

* Test broken url

* Break another source

* Good!

* Rename alt_sources variable

* Use SPC_DOWNLOAD_RETRIES instead of SPC_RETRY_TIME

* Apply new curlExec args
This commit is contained in:
Jerry Ma 2025-04-22 20:25:44 +08:00 committed by GitHub
parent 637ae89583
commit 455ed7d503
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 74 additions and 19 deletions

View File

@ -905,7 +905,6 @@
"path": "php-src/ext/swow-src",
"rev": "ci",
"url": "https://github.com/swow/swow",
"patch": "patchSwow",
"license": {
"type": "file",
"path": "LICENSE"

View File

@ -40,7 +40,7 @@ class Extension
// set source_dir for builtin
if ($ext_type === 'builtin') {
$this->source_dir = SOURCE_PATH . '/php-src/ext/' . $this->name;
} else {
} elseif ($ext_type === 'external') {
$source = Config::getExt($this->name, 'source');
if ($source === null) {
throw new RuntimeException("{$ext_type} extension {$name} source not found");
@ -48,6 +48,8 @@ class Extension
$source_path = Config::getSource($source)['path'] ?? null;
$source_path = $source_path === null ? SOURCE_PATH . '/' . $source : SOURCE_PATH . '/' . $source_path;
$this->source_dir = $source_path;
} else {
$this->source_dir = SOURCE_PATH . '/php-src';
}
}

View File

@ -43,6 +43,7 @@ class DownloadCommand extends BaseCommand
$this->addOption('ignore-cache-sources', null, InputOption::VALUE_OPTIONAL, 'Ignore some source caches, comma separated, e.g "php-src,curl,openssl"', false);
$this->addOption('retry', 'R', InputOption::VALUE_REQUIRED, 'Set retry time when downloading failed (default: 0)', '0');
$this->addOption('prefer-pre-built', 'P', null, 'Download pre-built libraries when available');
$this->addOption('no-alt', null, null, 'Do not download alternative sources');
}
/**
@ -95,22 +96,7 @@ class DownloadCommand extends BaseCommand
{
try {
if ($this->getOption('clean')) {
logger()->warning('You are doing some operations that not recoverable: removing directories below');
logger()->warning(SOURCE_PATH);
logger()->warning(DOWNLOAD_PATH);
logger()->warning(BUILD_ROOT_PATH);
logger()->alert('I will remove these dir after 5 seconds !');
sleep(5);
if (PHP_OS_FAMILY === 'Windows') {
f_passthru('rmdir /s /q ' . SOURCE_PATH);
f_passthru('rmdir /s /q ' . DOWNLOAD_PATH);
f_passthru('rmdir /s /q ' . BUILD_ROOT_PATH);
} else {
f_passthru('rm -rf ' . SOURCE_PATH . '/*');
f_passthru('rm -rf ' . DOWNLOAD_PATH . '/*');
f_passthru('rm -rf ' . BUILD_ROOT_PATH . '/*');
}
return static::FAILURE;
return $this->_clean();
}
// --from-zip
@ -249,7 +235,24 @@ class DownloadCommand extends BaseCommand
logger()->warning("Pre-built content not found for {$source}, fallback to source download");
}
logger()->info("[{$ni}/{$cnt}] Downloading source {$source}");
Downloader::downloadSource($source, $config, $force_all || in_array($source, $force_list));
try {
Downloader::downloadSource($source, $config, $force_all || in_array($source, $force_list));
} /* @noinspection PhpRedundantCatchClauseInspection */ catch (DownloaderException|RuntimeException $e) {
// if `--no-alt` option is set, we will not download alternative sources
if ($this->getOption('no-alt')) {
throw $e;
}
// if download failed, we will try to download alternative sources
logger()->warning("Download failed: {$e->getMessage()}");
logger()->notice("Trying to download alternative sources for {$source}");
$alt_sources = Config::getSource($source)['alt'] ?? null;
if ($alt_sources === null) {
$alt_config = array_merge($config, $this->getDefaultAlternativeSource($source));
} else {
$alt_config = array_merge($config, $alt_sources);
}
Downloader::downloadSource($source, $alt_config, $force_all || in_array($source, $force_list));
}
}
}
$time = round(microtime(true) - START_TIME, 3);
@ -368,4 +371,50 @@ class DownloadCommand extends BaseCommand
}
return null;
}
/**
* @throws RuntimeException
*/
private function _clean(): int
{
logger()->warning('You are doing some operations that not recoverable: removing directories below');
logger()->warning(SOURCE_PATH);
logger()->warning(DOWNLOAD_PATH);
logger()->warning(BUILD_ROOT_PATH);
logger()->alert('I will remove these dir after 5 seconds !');
sleep(5);
if (PHP_OS_FAMILY === 'Windows') {
f_passthru('rmdir /s /q ' . SOURCE_PATH);
f_passthru('rmdir /s /q ' . DOWNLOAD_PATH);
f_passthru('rmdir /s /q ' . BUILD_ROOT_PATH);
} else {
f_passthru('rm -rf ' . SOURCE_PATH . '/*');
f_passthru('rm -rf ' . DOWNLOAD_PATH . '/*');
f_passthru('rm -rf ' . BUILD_ROOT_PATH . '/*');
}
return static::FAILURE;
}
private function getDefaultAlternativeSource(string $source_name): array
{
return [
'type' => 'custom',
'func' => function (bool $force, array $source, int $download_as) use ($source_name) {
logger()->debug("Fetching alternative source for {$source_name}");
// get from dl.static-php.dev
$url = "https://dl.static-php.dev/static-php-cli/deps/spc-download-mirror/{$source_name}/?format=json";
$json = json_decode(Downloader::curlExec(url: $url, retries: intval(getenv('SPC_DOWNLOAD_RETRIES') ?: 0)), true);
if (!is_array($json)) {
throw new RuntimeException('failed http fetch');
}
$item = $json[0] ?? null;
if ($item === null) {
throw new RuntimeException('failed to parse json');
}
$full_url = 'https://dl.static-php.dev' . $item['full_path'];
$filename = basename($item['full_path']);
Downloader::downloadFile($source_name, $full_url, $filename, $source['path'] ?? null, $download_as);
},
];
}
}

View File

@ -477,6 +477,11 @@ class Downloader
);
break;
case 'custom': // Custom download method, like API-based download or other
if (isset($source['func']) && is_callable($source['func'])) {
$source['name'] = $name;
$source['func']($force, $source, $download_as);
break;
}
$classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/source', 'SPC\store\source');
foreach ($classes as $class) {
if (is_a($class, CustomSourceBase::class, true) && $class::NAME === $name) {