mirror of
https://gh.wpcy.net/https://github.com/fairpm/aspirecloud.git
synced 2026-06-20 02:22:28 +08:00
128 lines
4.1 KiB
PHP
128 lines
4.1 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Values\Packages\FairMetadata;
|
|
use App\Values\Packages\PackageData;
|
|
use Closure;
|
|
use Exception;
|
|
use Illuminate\Pipeline\Pipeline;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Http;
|
|
|
|
use function Safe\ini_set;
|
|
|
|
class PackageRepoIndexCommand extends Command
|
|
{
|
|
protected $signature = 'package:repo-index {--stop-on-first-error}';
|
|
|
|
protected $description = 'Import packages from FAIR repositories';
|
|
|
|
private string $currentRepo = '';
|
|
|
|
private int $errors = 0;
|
|
|
|
private int $loaded = 0;
|
|
|
|
public function handle(Pipeline $pipeline): void
|
|
{
|
|
ini_set('memory_limit', '-1');
|
|
|
|
$repos = config('fair.repos', []);
|
|
|
|
if (empty($repos)) {
|
|
$this->fail('No FAIR repositories configured. Update the FAIR_REPOS environment variable.');
|
|
}
|
|
|
|
$stages = [
|
|
$this->readPackageMetadata(...),
|
|
$this->createPackage(...),
|
|
];
|
|
|
|
assert(is_iterable($repos));
|
|
foreach ($repos as $repo) {
|
|
assert(is_string($repo));
|
|
$this->currentRepo = $repo;
|
|
try {
|
|
$packages = $this->getRepoPackages($repo);
|
|
foreach ($packages as $did) {
|
|
try {
|
|
DB::transaction(
|
|
fn() => $pipeline
|
|
->send($did)
|
|
->through($stages)
|
|
->thenReturn(),
|
|
);
|
|
} catch (Exception $e) {
|
|
$this->errors++;
|
|
$this->error("Package $did: {$e->getMessage()}");
|
|
$this->option('stop-on-first-error') and $this->fail('Errors encountered -- aborting.');
|
|
}
|
|
}
|
|
} catch (Exception $e) {
|
|
$this->errors++;
|
|
$this->error("Repo $this->currentRepo: {$e->getMessage()}");
|
|
$this->option('stop-on-first-error') and $this->fail('Errors encountered -- aborting.');
|
|
}
|
|
}
|
|
|
|
if ($this->errors > 0) {
|
|
$this->fail("Indexed $this->loaded packages; $this->errors errors");
|
|
}
|
|
|
|
$this->info("Indexed $this->loaded packages.");
|
|
}
|
|
|
|
/** @return array<string, string> */
|
|
private function getRepoPackages(string $repoUrl): array
|
|
{
|
|
$this->info("Fetching packages from $repoUrl");
|
|
|
|
$response = HTTP::withUrlParameters([
|
|
'repoUrl' => rtrim($repoUrl, '/'),
|
|
'path' => trim(config('fair.paths.packages', '/wp-json/minifair/v1/packages'), '/'),
|
|
])->withHeaders(['Accept' => 'application/json'])
|
|
->get('{+repoUrl}/{+path}');
|
|
|
|
if ($response->failed()) {
|
|
throw new Exception("Failed to fetch $repoUrl");
|
|
}
|
|
|
|
$data = $response->json();
|
|
if (!is_array($data)) {
|
|
throw new Exception("Invalid JSON from $repoUrl");
|
|
}
|
|
return $data;
|
|
}
|
|
|
|
private function readPackageMetadata(string $did, Closure $next): void
|
|
{
|
|
$this->info("Fetching package $did metadata from $this->currentRepo");
|
|
|
|
$response = HTTP::withUrlParameters([
|
|
'repoUrl' => rtrim($this->currentRepo, '/'),
|
|
'path' => trim(config('fair.paths.packages', '/wp-json/minifair/v1/packages'), '/'),
|
|
'did' => $did,
|
|
])->withHeaders(['Accept' => 'application/json'])
|
|
->get('{+repoUrl}/{+path}/{+did}');
|
|
|
|
if ($response->failed()) {
|
|
throw new Exception("Failed to fetch package metadata from $this->currentRepo");
|
|
}
|
|
$metadata = $response->json();
|
|
if (!is_array($metadata)) {
|
|
throw new Exception("Invalid JSON from $this->currentRepo");
|
|
}
|
|
$next($metadata);
|
|
}
|
|
|
|
/** @param array<string, mixed> $metadata */
|
|
private function createPackage(array $metadata, Closure $next): void
|
|
{
|
|
$fairMetadata = FairMetadata::from($metadata);
|
|
$package = PackageData::from($fairMetadata);
|
|
$this->loaded++;
|
|
$next($package);
|
|
}
|
|
}
|