Skip to content

Commit 605c06f

Browse files
committed
Add pie support for downloading sources
1 parent 8923077 commit 605c06f

File tree

2 files changed

+145
-155
lines changed

2 files changed

+145
-155
lines changed

config/source.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -999,9 +999,8 @@
999999
}
10001000
},
10011001
"spx": {
1002-
"type": "git",
1003-
"rev": "master",
1004-
"url": "https://github.com/NoiseByNorthwest/php-spx.git",
1002+
"type": "pie",
1003+
"repo": "noisebynorthwest/php-spx",
10051004
"path": "php-src/ext/spx",
10061005
"license": {
10071006
"type": "file",

src/SPC/store/Downloader.php

Lines changed: 143 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,42 @@
1616
*/
1717
class Downloader
1818
{
19+
/**
20+
* Get latest version from PIE config (Packagist)
21+
*
22+
* @param string $name Source name
23+
* @param array $source Source meta info: [repo]
24+
* @return array<int, string> [url, filename]
25+
*/
26+
public static function getPIEInfo(string $name, array $source): array
27+
{
28+
$packagist_url = "https://repo.packagist.org/p2/{$source['repo']}.json";
29+
logger()->debug("Fetching {$name} source from packagist index: {$packagist_url}");
30+
$data = json_decode(self::curlExec(
31+
url: $packagist_url,
32+
retries: self::getRetryAttempts()
33+
), true);
34+
if (!isset($data['packages'][$source['repo']]) || !is_array($data['packages'][$source['repo']])) {
35+
throw new DownloaderException("failed to find {$name} repo info from packagist");
36+
}
37+
// get the first version
38+
$first = $data['packages'][$source['repo']][0] ?? [];
39+
// check 'type' => 'php-ext' or contains 'php-ext' key
40+
if (!isset($first['php-ext'])) {
41+
throw new DownloaderException("failed to find {$name} php-ext info from packagist, maybe not a php extension package");
42+
}
43+
// get download link from dist
44+
$dist_url = $first['dist']['url'] ?? null;
45+
$dist_type = $first['dist']['type'] ?? null;
46+
if (!$dist_url || !$dist_type) {
47+
throw new DownloaderException("failed to find {$name} dist info from packagist");
48+
}
49+
$name = str_replace('/', '_', $source['repo']);
50+
$version = $first['version'] ?? 'unknown';
51+
// file name use: $name-$version.$dist_type
52+
return [$dist_url, "{$name}-{$version}.{$dist_type}"];
53+
}
54+
1955
/**
2056
* Get latest version from BitBucket tag
2157
*
@@ -317,84 +353,7 @@ public static function downloadPackage(string $name, ?array $pkg = null, bool $f
317353
if (self::isAlreadyDownloaded($name, $force, SPC_DOWNLOAD_PACKAGE)) {
318354
return;
319355
}
320-
321-
try {
322-
switch ($pkg['type']) {
323-
case 'bitbuckettag': // BitBucket Tag
324-
[$url, $filename] = self::getLatestBitbucketTag($name, $pkg);
325-
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null, SPC_DOWNLOAD_PACKAGE);
326-
break;
327-
case 'ghtar': // GitHub Release (tar)
328-
[$url, $filename] = self::getLatestGithubTarball($name, $pkg);
329-
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null, SPC_DOWNLOAD_PACKAGE, hooks: [[CurlHook::class, 'setupGithubToken']]);
330-
break;
331-
case 'ghtagtar': // GitHub Tag (tar)
332-
[$url, $filename] = self::getLatestGithubTarball($name, $pkg, 'tags');
333-
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null, SPC_DOWNLOAD_PACKAGE, hooks: [[CurlHook::class, 'setupGithubToken']]);
334-
break;
335-
case 'ghrel': // GitHub Release (uploaded)
336-
[$url, $filename] = self::getLatestGithubRelease($name, $pkg);
337-
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null, SPC_DOWNLOAD_PACKAGE, ['Accept: application/octet-stream'], [[CurlHook::class, 'setupGithubToken']]);
338-
break;
339-
case 'filelist': // Basic File List (regex based crawler)
340-
[$url, $filename] = self::getFromFileList($name, $pkg);
341-
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null, SPC_DOWNLOAD_PACKAGE);
342-
break;
343-
case 'url': // Direct download URL
344-
$url = $pkg['url'];
345-
$filename = $pkg['filename'] ?? basename($pkg['url']);
346-
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null, SPC_DOWNLOAD_PACKAGE);
347-
break;
348-
case 'git': // Git repo
349-
self::downloadGit(
350-
$name,
351-
$pkg['url'],
352-
$pkg['rev'],
353-
$pkg['submodules'] ?? null,
354-
$pkg['extract'] ?? null,
355-
self::getRetryAttempts(),
356-
SPC_DOWNLOAD_PRE_BUILT
357-
);
358-
break;
359-
case 'local':
360-
// Local directory, do nothing, just lock it
361-
logger()->debug("Locking local source {$name}");
362-
LockFile::lockSource($name, [
363-
'source_type' => SPC_SOURCE_LOCAL,
364-
'dirname' => $pkg['dirname'],
365-
'move_path' => $pkg['extract'] ?? null,
366-
'lock_as' => SPC_DOWNLOAD_PACKAGE,
367-
]);
368-
break;
369-
case 'custom': // Custom download method, like API-based download or other
370-
$classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/pkg', 'SPC\store\pkg');
371-
if (isset($pkg['func']) && is_callable($pkg['func'])) {
372-
$pkg['name'] = $name;
373-
$pkg['func']($force, $pkg, SPC_DOWNLOAD_PACKAGE);
374-
break;
375-
}
376-
foreach ($classes as $class) {
377-
if (is_a($class, CustomPackage::class, true) && $class !== CustomPackage::class) {
378-
$cls = new $class();
379-
if (in_array($name, $cls->getSupportName())) {
380-
(new $class())->fetch($name, $force, $pkg);
381-
break;
382-
}
383-
}
384-
}
385-
break;
386-
default:
387-
throw new DownloaderException('unknown source type: ' . $pkg['type']);
388-
}
389-
} catch (\Throwable $e) {
390-
// Because sometimes files downloaded through the command line are not automatically deleted after a failure.
391-
// Here we need to manually delete the file if it is detected to exist.
392-
if (isset($filename) && file_exists(DOWNLOAD_PATH . '/' . $filename)) {
393-
logger()->warning('Deleting download file: ' . $filename);
394-
unlink(DOWNLOAD_PATH . '/' . $filename);
395-
}
396-
throw new DownloaderException('Download failed! ' . $e->getMessage());
397-
}
356+
self::downloadByType($pkg['type'], $name, $pkg, $force, SPC_DOWNLOAD_PACKAGE);
398357
}
399358

400359
/**
@@ -439,80 +398,7 @@ public static function downloadSource(string $name, ?array $source = null, bool
439398
return;
440399
}
441400

442-
try {
443-
switch ($source['type']) {
444-
case 'bitbuckettag': // BitBucket Tag
445-
[$url, $filename] = self::getLatestBitbucketTag($name, $source);
446-
self::downloadFile($name, $url, $filename, $source['path'] ?? null, $download_as);
447-
break;
448-
case 'ghtar': // GitHub Release (tar)
449-
[$url, $filename] = self::getLatestGithubTarball($name, $source);
450-
self::downloadFile($name, $url, $filename, $source['path'] ?? null, $download_as, hooks: [[CurlHook::class, 'setupGithubToken']]);
451-
break;
452-
case 'ghtagtar': // GitHub Tag (tar)
453-
[$url, $filename] = self::getLatestGithubTarball($name, $source, 'tags');
454-
self::downloadFile($name, $url, $filename, $source['path'] ?? null, $download_as, hooks: [[CurlHook::class, 'setupGithubToken']]);
455-
break;
456-
case 'ghrel': // GitHub Release (uploaded)
457-
[$url, $filename] = self::getLatestGithubRelease($name, $source);
458-
self::downloadFile($name, $url, $filename, $source['path'] ?? null, $download_as, ['Accept: application/octet-stream'], [[CurlHook::class, 'setupGithubToken']]);
459-
break;
460-
case 'filelist': // Basic File List (regex based crawler)
461-
[$url, $filename] = self::getFromFileList($name, $source);
462-
self::downloadFile($name, $url, $filename, $source['path'] ?? null, $download_as);
463-
break;
464-
case 'url': // Direct download URL
465-
$url = $source['url'];
466-
$filename = $source['filename'] ?? basename($source['url']);
467-
self::downloadFile($name, $url, $filename, $source['path'] ?? null, $download_as);
468-
break;
469-
case 'git': // Git repo
470-
self::downloadGit(
471-
$name,
472-
$source['url'],
473-
$source['rev'],
474-
$source['submodules'] ?? null,
475-
$source['path'] ?? null,
476-
self::getRetryAttempts(),
477-
$download_as
478-
);
479-
break;
480-
case 'local':
481-
// Local directory, do nothing, just lock it
482-
logger()->debug("Locking local source {$name}");
483-
LockFile::lockSource($name, [
484-
'source_type' => SPC_SOURCE_LOCAL,
485-
'dirname' => $source['dirname'],
486-
'move_path' => $source['extract'] ?? null,
487-
'lock_as' => $download_as,
488-
]);
489-
break;
490-
case 'custom': // Custom download method, like API-based download or other
491-
if (isset($source['func']) && is_callable($source['func'])) {
492-
$source['name'] = $name;
493-
$source['func']($force, $source, $download_as);
494-
break;
495-
}
496-
$classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/source', 'SPC\store\source');
497-
foreach ($classes as $class) {
498-
if (is_a($class, CustomSourceBase::class, true) && $class::NAME === $name) {
499-
(new $class())->fetch($force, $source, $download_as);
500-
break;
501-
}
502-
}
503-
break;
504-
default:
505-
throw new DownloaderException('unknown source type: ' . $source['type']);
506-
}
507-
} catch (\Throwable $e) {
508-
// Because sometimes files downloaded through the command line are not automatically deleted after a failure.
509-
// Here we need to manually delete the file if it is detected to exist.
510-
if (isset($filename) && file_exists(DOWNLOAD_PATH . '/' . $filename)) {
511-
logger()->warning('Deleting download file: ' . $filename);
512-
unlink(DOWNLOAD_PATH . '/' . $filename);
513-
}
514-
throw new DownloaderException('Download failed! ' . $e->getMessage());
515-
}
401+
self::downloadByType($source['type'], $name, $source, $force, $download_as);
516402
}
517403

518404
/**
@@ -713,4 +599,109 @@ private static function isAlreadyDownloaded(string $name, bool $force, int $down
713599
}
714600
return false;
715601
}
602+
603+
/**
604+
* Download by type.
605+
*
606+
* @param string $type Types
607+
* @param string $name Download item name
608+
* @param array{
609+
* url?: string,
610+
* repo?: string,
611+
* rev?: string,
612+
* path?: string,
613+
* filename?: string,
614+
* dirname?: string,
615+
* match?: string,
616+
* prefer-stable?: bool,
617+
* extract?: string,
618+
* submodules?: array<string>,
619+
* provide-pre-built?: bool,
620+
* func?: ?callable,
621+
* license?: array
622+
* } $conf Download item config
623+
* @param bool $force Force download
624+
* @param int $download_as Lock source type
625+
*/
626+
private static function downloadByType(string $type, string $name, array $conf, bool $force, int $download_as): void
627+
{
628+
try {
629+
switch ($type) {
630+
case 'pie': // Packagist
631+
[$url, $filename] = self::getPIEInfo($name, $conf);
632+
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as, hooks: [[CurlHook::class, 'setupGithubToken']]);
633+
break;
634+
case 'bitbuckettag': // BitBucket Tag
635+
[$url, $filename] = self::getLatestBitbucketTag($name, $conf);
636+
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as);
637+
break;
638+
case 'ghtar': // GitHub Release (tar)
639+
[$url, $filename] = self::getLatestGithubTarball($name, $conf);
640+
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as, hooks: [[CurlHook::class, 'setupGithubToken']]);
641+
break;
642+
case 'ghtagtar': // GitHub Tag (tar)
643+
[$url, $filename] = self::getLatestGithubTarball($name, $conf, 'tags');
644+
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as, hooks: [[CurlHook::class, 'setupGithubToken']]);
645+
break;
646+
case 'ghrel': // GitHub Release (uploaded)
647+
[$url, $filename] = self::getLatestGithubRelease($name, $conf);
648+
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as, ['Accept: application/octet-stream'], [[CurlHook::class, 'setupGithubToken']]);
649+
break;
650+
case 'filelist': // Basic File List (regex based crawler)
651+
[$url, $filename] = self::getFromFileList($name, $conf);
652+
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as);
653+
break;
654+
case 'url': // Direct download URL
655+
$url = $conf['url'];
656+
$filename = $conf['filename'] ?? basename($conf['url']);
657+
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as);
658+
break;
659+
case 'git': // Git repo
660+
self::downloadGit($name, $conf['url'], $conf['rev'], $conf['submodules'] ?? null, $conf['path'] ?? $conf['extract'] ?? null, self::getRetryAttempts(), $download_as);
661+
break;
662+
case 'local': // Local directory, do nothing, just lock it
663+
LockFile::lockSource($name, [
664+
'source_type' => SPC_SOURCE_LOCAL,
665+
'dirname' => $conf['dirname'],
666+
'move_path' => $conf['path'] ?? $conf['extract'] ?? null,
667+
'lock_as' => $download_as,
668+
]);
669+
break;
670+
case 'custom': // Custom download method, like API-based download or other
671+
if (isset($conf['func']) && is_callable($conf['func'])) {
672+
$conf['name'] = $name;
673+
$conf['func']($force, $conf, $download_as);
674+
break;
675+
}
676+
$classes = [
677+
...FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/source', 'SPC\store\source'),
678+
...FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/pkg', 'SPC\store\pkg'),
679+
];
680+
foreach ($classes as $class) {
681+
if (is_a($class, CustomSourceBase::class, true) && $class::NAME === $name) {
682+
(new $class())->fetch($force, $conf, $download_as);
683+
break;
684+
}
685+
if (is_a($class, CustomPackage::class, true) && $class !== CustomPackage::class) {
686+
$cls = new $class();
687+
if (in_array($name, $cls->getSupportName())) {
688+
(new $class())->fetch($name, $force, $conf);
689+
break;
690+
}
691+
}
692+
}
693+
break;
694+
default:
695+
throw new DownloaderException("Unknown download type: {$type}");
696+
}
697+
} catch (\Throwable $e) {
698+
// Because sometimes files downloaded through the command line are not automatically deleted after a failure.
699+
// Here we need to manually delete the file if it is detected to exist.
700+
if (isset($filename) && file_exists(DOWNLOAD_PATH . '/' . $filename)) {
701+
logger()->warning("Deleting download file: {$filename}");
702+
unlink(DOWNLOAD_PATH . '/' . $filename);
703+
}
704+
throw new DownloaderException("Download failed: {$e->getMessage()}");
705+
}
706+
}
716707
}

0 commit comments

Comments
 (0)