diff --git a/README.md b/README.md
index 26bab6b..4100fe8 100644
--- a/README.md
+++ b/README.md
@@ -39,19 +39,54 @@ composer require internal/dload -W
./vendor/bin/dload get dolt
```
-### Configure preset for the project (WIP)
+### Configure preset for the project
Create `dload.xml` file in the root of the project with the following content:
```xml
-
+
+
+
+
```
-Download all the software from the preset:
+There are two software packages to download: `temporal` and `rr` with version `^2.12.0`.
+Optionally, you may specify the version of the software package using Composer versioning syntax.
+To download all the configured software, run `dload get` without arguments:
```bash
./vendor/bin/dload get
```
+
+### Custom software registry
+
+```xml
+
+
+
+
+
+
+
+
+
+```
+
+### GitHub Token
+
+To increase the rate limit for GitHub API, you can specify the token in the environment variable `GITHUB_TOKEN`:
+
+```bash
+GITHUB_TOKEN=your_token_here ./vendor/bin/dload get
+```
diff --git a/dload.xml b/dload.xml
index 55ea365..c416903 100644
--- a/dload.xml
+++ b/dload.xml
@@ -1,17 +1,19 @@
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
diff --git a/psalm-baseline.xml b/psalm-baseline.xml
index 54e8b20..983c3b9 100644
--- a/psalm-baseline.xml
+++ b/psalm-baseline.xml
@@ -1,25 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ $toDownload[$software]
+ ?? DownloadConfig::fromSoftwareId((string) $software),
+ $input->getArgument(self::ARG_SOFTWARE),
+ )]]>
+
-
+ getArgument(self::ARG_SOFTWARE)]]>
+
+
+
-
+
-
- getArgument('binary')]]>
- getOption('path')]]>
-
+
+ ]]>
+
-
-
+
+
+
+ cancelling]]>
+
+
+
+
+
+
+
+ type]]>
+ uri]]>
+
+
+
+
+
+
+ repositories]]>
+
+
+ homepage]]>
+
pattern]]>
+
+ files]]>
+
@@ -34,12 +79,33 @@
+
+
+
+
+
+
+
+
+ File::fromArray($fileArray),
+ $softwareArray['files'] ?? [],
+ )]]>
+ Repository::fromArray($repositoryArray),
+ $softwareArray['repositories'] ?? [],
+ )]]>
+
+
+
+
+
@@ -69,10 +135,16 @@
repoConfig->assetPattern]]>
+
+
+
+
+
+
@@ -80,6 +152,12 @@
($context->onProgress)(]]>
+
+ repoConfig]]>
+
+
+ repoConfig]]>
+
@@ -92,6 +170,15 @@
+
+
+
+
+
+
+
+
+
addOption('config', null, InputOption::VALUE_OPTIONAL, 'Path to the configuration file');
+ }
+
protected function execute(
InputInterface $input,
OutputInterface $output,
): int {
$this->logger = new Logger($output);
$this->container = $container = Bootstrap::init()->withConfig(
- xml: \dirname(__DIR__, 2) . '/dload.xml',
+ xml: $this->getConfigFile($input),
inputOptions: $input->getOptions(),
inputArguments: $input->getArguments(),
environment: \getenv(),
)->finish();
+
$container->set($input, InputInterface::class);
$container->set($output, OutputInterface::class);
$container->set(new SymfonyStyle($input, $output), StyleInterface::class);
@@ -40,4 +48,25 @@ protected function execute(
return Command::SUCCESS;
}
+
+ /**
+ * @return non-empty-string|null Path to the configuration file
+ */
+ private function getConfigFile(InputInterface $input): ?string
+ {
+ /** @var string|null $config */
+ $config = $input->getOption('config');
+ $isConfigured = $config !== null;
+ $config ??= './dload.xml';
+
+ if (\is_file($config)) {
+ return $config;
+ }
+
+ $isConfigured and throw new \InvalidArgumentException(
+ 'Configuration file not found: ' . $config,
+ );
+
+ return null;
+ }
}
diff --git a/src/Command/Get.php b/src/Command/Get.php
index deb4888..c5284cf 100644
--- a/src/Command/Get.php
+++ b/src/Command/Get.php
@@ -6,6 +6,8 @@
use Internal\DLoad\DLoad;
use Internal\DLoad\Module\Common\Architecture;
+use Internal\DLoad\Module\Common\Config\Action\Download as DownloadConfig;
+use Internal\DLoad\Module\Common\Config\Actions;
use Internal\DLoad\Module\Common\OperatingSystem;
use Internal\DLoad\Module\Common\Stability;
use Symfony\Component\Console\Attribute\AsCommand;
@@ -25,9 +27,12 @@
)]
final class Get extends Base implements SignalableCommandInterface
{
+ private const ARG_SOFTWARE = 'software';
+
public function configure(): void
{
- $this->addArgument('binary', InputArgument::REQUIRED, 'Binary name, e.g. "rr", "dolt", "temporal" etc.');
+ parent::configure();
+ $this->addArgument(self::ARG_SOFTWARE, InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Software name, e.g. "rr", "dolt", "temporal" etc.');
$this->addOption('path', null, InputOption::VALUE_OPTIONAL, 'Path to store the binary, e.g. "./bin"', ".");
$this->addOption('arch', null, InputOption::VALUE_OPTIONAL, 'Architecture, e.g. "amd64", "arm64" etc.');
$this->addOption('os', null, InputOption::VALUE_OPTIONAL, 'Operating system, e.g. "linux", "darwin" etc.');
@@ -61,22 +66,49 @@ public function getSubscribedSignals(): array
protected function execute(InputInterface $input, OutputInterface $output): int
{
parent::execute($input, $output);
-
$container = $this->container;
- $output->writeln('Binary to load: ' . $input->getArgument('binary'));
- $output->writeln('Path to store the binary: ' . $input->getOption('path'));
+ /** @var Actions $actionsConfig */
+ $actionsConfig = $container->get(Actions::class);
+ $actions = $this->getDownloadActions($input, $actionsConfig);
+
$output->writeln('Architecture: ' . $container->get(Architecture::class)->name);
$output->writeln(' Op. system: ' . $container->get(OperatingSystem::class)->name);
$output->writeln(' Stability: ' . $container->get(Stability::class)->name);
+ $actions === [] and throw new \RuntimeException('No software to download.');
+
/** @var DLoad $dload */
$dload = $container->get(DLoad::class);
- $binary = $input->getArgument('binary');
- $dload->addTask($binary);
+ foreach ($actions as $action) {
+ $dload->addTask($action);
+ }
$dload->run();
return Command::SUCCESS;
}
+
+ /**
+ * @return list
+ */
+ private function getDownloadActions(InputInterface $input, Actions $actionsConfig): array
+ {
+ $argument = $input->getArgument(self::ARG_SOFTWARE);
+ if ($argument === []) {
+ // Use configured actions if CLI arguments are empty
+ return $actionsConfig->downloads;
+ }
+
+ $toDownload = [];
+ foreach ($actionsConfig->downloads as $action) {
+ $toDownload[$action->software] = $action;
+ }
+
+ return \array_map(
+ static fn(mixed $software): DownloadConfig => $toDownload[$software]
+ ?? DownloadConfig::fromSoftwareId((string) $software),
+ $input->getArgument(self::ARG_SOFTWARE),
+ );
+ }
}
diff --git a/src/Command/ListSoftware.php b/src/Command/ListSoftware.php
index 5edce10..f2a211f 100644
--- a/src/Command/ListSoftware.php
+++ b/src/Command/ListSoftware.php
@@ -35,6 +35,7 @@ protected function execute(
/** @var Software $software */
foreach ($registry->getIterator() as $software) {
$output->writeln("{$software->getId()}> $software->name");
+ $software->homepage and $output->writeln("Homepage: $software->homepage>");
foreach ($software->repositories as $repo) {
$output->writeln("{$repo->type}: {$repo->uri}>");
diff --git a/src/DLoad.php b/src/DLoad.php
index 2670b57..cabd90a 100644
--- a/src/DLoad.php
+++ b/src/DLoad.php
@@ -5,14 +5,16 @@
namespace Internal\DLoad;
use Internal\DLoad\Module\Archive\ArchiveFactory;
-use Internal\DLoad\Module\Common\Config\Destination;
+use Internal\DLoad\Module\Common\Config\Action\Download as DownloadConfig;
use Internal\DLoad\Module\Common\Config\Embed\File;
use Internal\DLoad\Module\Common\Config\Embed\Software;
+use Internal\DLoad\Module\Common\Input\Destination;
use Internal\DLoad\Module\Downloader\Downloader;
use Internal\DLoad\Module\Downloader\SoftwareCollection;
use Internal\DLoad\Module\Downloader\Task\DownloadResult;
use Internal\DLoad\Module\Downloader\Task\DownloadTask;
use Internal\DLoad\Module\Downloader\TaskManager;
+use Internal\DLoad\Service\Logger;
use React\Promise\PromiseInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\StyleInterface;
@@ -29,6 +31,7 @@ final class DLoad
public bool $useMock = false;
public function __construct(
+ private readonly Logger $logger,
private readonly TaskManager $taskManager,
private readonly SoftwareCollection $softwareCollection,
private readonly Downloader $downloader,
@@ -38,18 +41,16 @@ public function __construct(
private readonly StyleInterface $io,
) {}
- public function addTask(
- string $softwareName,
- ): void {
- $this->useMock and $softwareName = 'rr';
- $this->taskManager->addTask(function () use ($softwareName): void {
+ public function addTask(DownloadConfig $action): void
+ {
+ $this->taskManager->addTask(function () use ($action): void {
// Find Software
- $software = $this->softwareCollection->findSoftware($softwareName) ?? throw new \RuntimeException(
+ $software = $this->softwareCollection->findSoftware($action->software) ?? throw new \RuntimeException(
'Software not found.',
);
// Create a Download task
- $task = $this->prepareDownloadTask($software);
+ $task = $this->prepareDownloadTask($software, $action);
// Extract files
($task->handler)()->then($this->prepareExtractTask($software));
@@ -61,7 +62,7 @@ public function run(): void
$this->taskManager->await();
}
- private function prepareDownloadTask(Software $software): DownloadTask
+ private function prepareDownloadTask(Software $software, DownloadConfig $action): DownloadTask
{
return $this->useMock
? new DownloadTask(
@@ -74,7 +75,7 @@ private function prepareDownloadTask(Software $software): DownloadTask
),
),
)
- : $this->downloader->download($software, static fn() => null);
+ : $this->downloader->download($software, $action, static fn() => null);
}
/**
@@ -86,6 +87,7 @@ private function prepareExtractTask(Software $software): \Closure
$fileInfo = $downloadResult->file;
$archive = $this->archiveFactory->create($fileInfo);
$extractor = $archive->extract();
+ $this->logger->info('Extracting %s', $fileInfo->getFilename());
while ($extractor->valid()) {
$file = $extractor->current();
diff --git a/src/Module/Common/Architecture.php b/src/Module/Common/Architecture.php
index c38b9f1..54a0a5e 100644
--- a/src/Module/Common/Architecture.php
+++ b/src/Module/Common/Architecture.php
@@ -4,7 +4,7 @@
namespace Internal\DLoad\Module\Common;
-use Internal\DLoad\Module\Common\Config\BuildInput;
+use Internal\DLoad\Module\Common\Input\Build;
use Internal\DLoad\Service\Factoriable;
/**
@@ -19,7 +19,7 @@ enum Architecture: string implements Factoriable
private const ERROR_UNKNOWN_ARCH = 'Current architecture `%s` may not be supported.';
- public static function create(BuildInput $config): self
+ public static function create(Build $config): self
{
return self::tryFrom((string) $config->arch) ?? self::fromGlobals();
}
diff --git a/src/Module/Common/Config/Action/Download.php b/src/Module/Common/Config/Action/Download.php
new file mode 100644
index 0000000..7c616d7
--- /dev/null
+++ b/src/Module/Common/Config/Action/Download.php
@@ -0,0 +1,31 @@
+software = $software;
+ return $action;
+ }
+}
diff --git a/src/Module/Common/Config/Actions.php b/src/Module/Common/Config/Actions.php
new file mode 100644
index 0000000..ec6daad
--- /dev/null
+++ b/src/Module/Common/Config/Actions.php
@@ -0,0 +1,18 @@
+ */
+ #[XPathEmbedList('/dload/actions/download', Download::class)]
+ public array $downloads = [];
+}
diff --git a/src/Module/Common/Config/SoftwareRegistry.php b/src/Module/Common/Config/CustomSoftwareRegistry.php
similarity index 53%
rename from src/Module/Common/Config/SoftwareRegistry.php
rename to src/Module/Common/Config/CustomSoftwareRegistry.php
index be299ce..e18b264 100644
--- a/src/Module/Common/Config/SoftwareRegistry.php
+++ b/src/Module/Common/Config/CustomSoftwareRegistry.php
@@ -4,12 +4,16 @@
namespace Internal\DLoad\Module\Common\Config;
+use Internal\DLoad\Module\Common\Internal\Attribute\XPath;
use Internal\DLoad\Module\Common\Internal\Attribute\XPathEmbedList;
-final class SoftwareRegistry
+final class CustomSoftwareRegistry
{
+ #[XPath('/dload/registry/@overwrite')]
+ public bool $overwrite = false;
+
/**
- * @var Embed\Software[]
+ * @var \Internal\DLoad\Module\Common\Config\Embed\Software[]
*/
#[XPathEmbedList('/dload/registry/software', Embed\Software::class)]
public array $software = [];
diff --git a/src/Module/Common/Config/DownloaderConfig.php b/src/Module/Common/Config/Downloader.php
similarity index 50%
rename from src/Module/Common/Config/DownloaderConfig.php
rename to src/Module/Common/Config/Downloader.php
index f9e9f86..855ca8b 100644
--- a/src/Module/Common/Config/DownloaderConfig.php
+++ b/src/Module/Common/Config/Downloader.php
@@ -4,7 +4,10 @@
namespace Internal\DLoad\Module\Common\Config;
-final class DownloaderConfig
+use Internal\DLoad\Module\Common\Internal\Attribute\XPath;
+
+final class Downloader
{
+ #[XPath('/dload/@temp-dir')]
public ?string $tmpDir = null;
}
diff --git a/src/Module/Common/Config/Embed/File.php b/src/Module/Common/Config/Embed/File.php
index ede6d45..3f4f60d 100644
--- a/src/Module/Common/Config/Embed/File.php
+++ b/src/Module/Common/Config/Embed/File.php
@@ -6,6 +6,12 @@
use Internal\DLoad\Module\Common\Internal\Attribute\XPath;
+/**
+ * @psalm-type FileArray = array{
+ * rename?: non-empty-string,
+ * pattern?: non-empty-string
+ * }
+ */
final class File
{
/**
@@ -16,4 +22,16 @@ final class File
#[XPath('@pattern')]
public string $pattern = '/^.*$/';
+
+ /**
+ * @param FileArray $fileArray
+ */
+ public static function fromArray(mixed $fileArray): self
+ {
+ $self = new self();
+ $self->rename = $fileArray['rename'] ?? null;
+ $self->pattern = $fileArray['pattern'] ?? '/^.*$/';
+
+ return $self;
+ }
}
diff --git a/src/Module/Common/Config/Embed/Repository.php b/src/Module/Common/Config/Embed/Repository.php
index 8742fe4..e5e8ae4 100644
--- a/src/Module/Common/Config/Embed/Repository.php
+++ b/src/Module/Common/Config/Embed/Repository.php
@@ -6,6 +6,13 @@
use Internal\DLoad\Module\Common\Internal\Attribute\XPath;
+/**
+ * @psalm-type RepositoryArray = array{
+ * type: non-empty-string,
+ * uri: non-empty-string,
+ * asset-pattern?: non-empty-string
+ * }
+ */
final class Repository
{
#[XPath('@type')]
@@ -16,4 +23,17 @@ final class Repository
#[XPath('@asset-pattern')]
public string $assetPattern = '/^.*$/';
+
+ /**
+ * @param RepositoryArray $repositoryArray
+ */
+ public static function fromArray(mixed $repositoryArray): self
+ {
+ $self = new self();
+ $self->type = $repositoryArray['type'] ?? 'github';
+ $self->uri = $repositoryArray['uri'];
+ $self->assetPattern = $repositoryArray['asset-pattern'] ?? '/^.*$/';
+
+ return $self;
+ }
}
diff --git a/src/Module/Common/Config/Embed/Software.php b/src/Module/Common/Config/Embed/Software.php
index c0894bb..42f336c 100644
--- a/src/Module/Common/Config/Embed/Software.php
+++ b/src/Module/Common/Config/Embed/Software.php
@@ -7,6 +7,18 @@
use Internal\DLoad\Module\Common\Internal\Attribute\XPath;
use Internal\DLoad\Module\Common\Internal\Attribute\XPathEmbedList;
+/**
+ * @psalm-import-type RepositoryArray from Repository
+ * @psalm-import-type FileArray from File
+ * @psalm-type SoftwareArray = array{
+ * name: non-empty-string,
+ * alias?: non-empty-string,
+ * homepage?: non-empty-string,
+ * description?: non-empty-string,
+ * repositories?: list,
+ * files?: list
+ * }
+ */
final class Software
{
/**
@@ -22,17 +34,42 @@ final class Software
#[XPath('@alias')]
public ?string $alias = null;
+ #[XPath('@homepage')]
+ public ?string $homepage = null;
+
#[XPath('@description')]
public string $description = '';
- /** @var list */
+ /** @var File */
#[XPathEmbedList('repository', Repository::class)]
public array $repositories = [];
- /** @var list */
+ /** @var File */
#[XPathEmbedList('file', File::class)]
public array $files = [];
+ /**
+ * @param SoftwareArray $softwareArray
+ */
+ public static function fromArray(mixed $softwareArray): self
+ {
+ $self = new self();
+ $self->name = $softwareArray['name'];
+ $self->alias = $softwareArray['alias'] ?? null;
+ $self->homepage = $softwareArray['homepage'] ?? null;
+ $self->description = $softwareArray['description'] ?? '';
+ $self->repositories = \array_map(
+ static fn(array $repositoryArray): Repository => Repository::fromArray($repositoryArray),
+ $softwareArray['repositories'] ?? [],
+ );
+ $self->files = \array_map(
+ static fn(array $fileArray): File => File::fromArray($fileArray),
+ $softwareArray['files'] ?? [],
+ );
+
+ return $self;
+ }
+
/**
* @return non-empty-string
*/
diff --git a/src/Module/Common/Config/GitHubConfig.php b/src/Module/Common/Config/GitHub.php
similarity index 90%
rename from src/Module/Common/Config/GitHubConfig.php
rename to src/Module/Common/Config/GitHub.php
index d123322..34706c8 100644
--- a/src/Module/Common/Config/GitHubConfig.php
+++ b/src/Module/Common/Config/GitHub.php
@@ -9,7 +9,7 @@
/**
* @internal
*/
-final class GitHubConfig
+final class GitHub
{
#[Env('GITHUB_TOKEN')]
public ?string $token = null;
diff --git a/src/Module/Common/Config/BuildInput.php b/src/Module/Common/Input/Build.php
similarity index 91%
rename from src/Module/Common/Config/BuildInput.php
rename to src/Module/Common/Input/Build.php
index e43aaa1..41bc720 100644
--- a/src/Module/Common/Config/BuildInput.php
+++ b/src/Module/Common/Input/Build.php
@@ -2,7 +2,7 @@
declare(strict_types=1);
-namespace Internal\DLoad\Module\Common\Config;
+namespace Internal\DLoad\Module\Common\Input;
use Internal\DLoad\Module\Common\Architecture;
use Internal\DLoad\Module\Common\Internal\Attribute\InputOption;
@@ -12,7 +12,7 @@
/**
* @internal
*/
-final class BuildInput
+final class Build
{
/**
* Use {@see Architecture} to get final value.
diff --git a/src/Module/Common/Config/Destination.php b/src/Module/Common/Input/Destination.php
similarity index 81%
rename from src/Module/Common/Config/Destination.php
rename to src/Module/Common/Input/Destination.php
index c1e2aed..ad0dc09 100644
--- a/src/Module/Common/Config/Destination.php
+++ b/src/Module/Common/Input/Destination.php
@@ -2,7 +2,7 @@
declare(strict_types=1);
-namespace Internal\DLoad\Module\Common\Config;
+namespace Internal\DLoad\Module\Common\Input;
use Internal\DLoad\Module\Common\Internal\Attribute\InputOption;
diff --git a/src/Module/Common/Internal/Injection/ConfigLoader.php b/src/Module/Common/Internal/Injection/ConfigLoader.php
index 203a5b1..8aaacc0 100644
--- a/src/Module/Common/Internal/Injection/ConfigLoader.php
+++ b/src/Module/Common/Internal/Injection/ConfigLoader.php
@@ -126,8 +126,12 @@ private function getXPath(XPath $attribute): mixed
private function getXPathEmbeddedList(XPathEmbedList $attribute): array
{
+ if ($this->xml === null) {
+ return [];
+ }
+
$result = [];
- $value = $this->xml?->xpath($attribute->path);
+ $value = $this->xml->xpath($attribute->path);
\is_array($value) or throw new \Exception(\sprintf('Invalid XPath `%s`', $attribute->path));
foreach ($value as $xml) {
diff --git a/src/Module/Common/OperatingSystem.php b/src/Module/Common/OperatingSystem.php
index d610665..6a2c2b4 100644
--- a/src/Module/Common/OperatingSystem.php
+++ b/src/Module/Common/OperatingSystem.php
@@ -4,7 +4,7 @@
namespace Internal\DLoad\Module\Common;
-use Internal\DLoad\Module\Common\Config\BuildInput;
+use Internal\DLoad\Module\Common\Input\Build;
use Internal\DLoad\Service\Factoriable;
/**
@@ -22,7 +22,7 @@ enum OperatingSystem: string implements Factoriable
private const ERROR_UNKNOWN_OS = 'Current OS `%s` may not be supported';
- public static function create(BuildInput $config): static
+ public static function create(Build $config): static
{
return self::tryFrom((string) $config->os) ?? self::fromGlobals();
}
diff --git a/src/Module/Common/Stability.php b/src/Module/Common/Stability.php
index fb15e60..d211558 100644
--- a/src/Module/Common/Stability.php
+++ b/src/Module/Common/Stability.php
@@ -4,7 +4,7 @@
namespace Internal\DLoad\Module\Common;
-use Internal\DLoad\Module\Common\Config\BuildInput;
+use Internal\DLoad\Module\Common\Input\Build;
use Internal\DLoad\Service\Factoriable;
/**
@@ -20,7 +20,7 @@ enum Stability: string implements Factoriable
case Alpha = 'alpha';
case Dev = 'dev';
- public static function create(BuildInput $config): static
+ public static function create(Build $config): static
{
return self::tryFrom((string) $config->stability) ?? self::fromGlobals();
}
diff --git a/src/Module/Downloader/Downloader.php b/src/Module/Downloader/Downloader.php
index c5e37b4..0f4bf81 100644
--- a/src/Module/Downloader/Downloader.php
+++ b/src/Module/Downloader/Downloader.php
@@ -6,7 +6,8 @@
use Internal\DLoad\Module\Archive\ArchiveFactory;
use Internal\DLoad\Module\Common\Architecture;
-use Internal\DLoad\Module\Common\Config\DownloaderConfig;
+use Internal\DLoad\Module\Common\Config\Action\Download as DownloadConfig;
+use Internal\DLoad\Module\Common\Config\Downloader as DownloaderConfig;
use Internal\DLoad\Module\Common\Config\Embed\Software;
use Internal\DLoad\Module\Common\OperatingSystem;
use Internal\DLoad\Module\Common\Stability;
@@ -44,11 +45,13 @@ public function __construct(
*/
public function download(
Software $software,
+ DownloadConfig $actionConfig,
\Closure $onProgress,
): DownloadTask {
$context = new DownloadContext(
software: $software,
onProgress: $onProgress,
+ actionConfig: $actionConfig,
);
$repositories = $software->repositories;
@@ -91,10 +94,21 @@ public function download(
private function processRepository(RepositoryInterface $repository, DownloadContext $context): \Closure
{
return function () use ($repository, $context): ReleaseInterface {
+ $this->logger->info(
+ 'Loading releases from `%s` repository %s',
+ $context->repoConfig->type,
+ $repository->getName(),
+ );
+
+ $releasesCollection = $repository->getReleases()
+ ->minimumStability($this->stability);
+
+ // Filter by version if specified
+ $context->actionConfig->version === null or $releasesCollection = $releasesCollection
+ ->satisfies($context->actionConfig->version);
+
/** @var ReleaseInterface[] $releases */
- $releases = $repository->getReleases()
- ->minimumStability($this->stability)
- ->sortByVersion()->toArray();
+ $releases = $releasesCollection->sortByVersion()->toArray();
$this->logger->debug('%d releases found.', \count($releases));
@@ -102,12 +116,13 @@ private function processRepository(RepositoryInterface $repository, DownloadCont
$releases === [] and throw new \RuntimeException('No relevant release found.');
$context->release = \array_shift($releases);
- $this->logger->debug('Trying to load release `%s`', $context->release->getName());
+ $this->logger->info('Loading release `%s`', $context->release->getName());
try {
await(coroutine($this->processRelease($context)));
return $context->release;
} catch (\Throwable $e) {
+ $this->logger->error('%s', $e->getMessage());
$this->logger->exception($e);
goto process_release;
}
@@ -154,7 +169,7 @@ private function processAsset(DownloadContext $context): \Closure
$temp = $this->getTempDirectory() . DIRECTORY_SEPARATOR . $context->asset->getName();
$file = new \SplFileObject($temp, 'wb+');
- $this->logger->debug('Downloading into ' . $temp);
+ $this->logger->info('Downloading into %s', $temp);
await(coroutine(
(static function () use ($context, $file): void {
diff --git a/src/Module/Downloader/Internal/DownloadContext.php b/src/Module/Downloader/Internal/DownloadContext.php
index 50b81bc..2ef349e 100644
--- a/src/Module/Downloader/Internal/DownloadContext.php
+++ b/src/Module/Downloader/Internal/DownloadContext.php
@@ -4,6 +4,7 @@
namespace Internal\DLoad\Module\Downloader\Internal;
+use Internal\DLoad\Module\Common\Config\Action\Download as DownloadConfig;
use Internal\DLoad\Module\Common\Config\Embed\Repository;
use Internal\DLoad\Module\Common\Config\Embed\Software;
use Internal\DLoad\Module\Downloader\Progress;
@@ -31,5 +32,6 @@ final class DownloadContext
public function __construct(
public readonly Software $software,
public readonly \Closure $onProgress,
+ public readonly DownloadConfig $actionConfig,
) {}
}
diff --git a/src/Module/Downloader/SoftwareCollection.php b/src/Module/Downloader/SoftwareCollection.php
index aad13ef..bce7f0f 100644
--- a/src/Module/Downloader/SoftwareCollection.php
+++ b/src/Module/Downloader/SoftwareCollection.php
@@ -4,8 +4,9 @@
namespace Internal\DLoad\Module\Downloader;
+use Internal\DLoad\Info;
+use Internal\DLoad\Module\Common\Config\CustomSoftwareRegistry;
use Internal\DLoad\Module\Common\Config\Embed\Software;
-use Internal\DLoad\Module\Common\Config\SoftwareRegistry;
use IteratorAggregate;
/**
@@ -13,19 +14,21 @@
*/
final class SoftwareCollection implements \IteratorAggregate, \Countable
{
- public function __construct(
- private readonly SoftwareRegistry $softwareRegistry,
- ) {}
+ /** @var array */
+ private array $registry = [];
- public function findSoftware(string $name): ?Software
+ public function __construct(CustomSoftwareRegistry $softwareRegistry)
{
- foreach ($this->softwareRegistry->software as $software) {
- if ($software->getId() === $name) {
- return $software;
- }
+ foreach ($softwareRegistry->software as $software) {
+ $this->registry[$software->getId()] = $software;
}
- return null;
+ $softwareRegistry->overwrite or $this->loadDefaultRegistry();
+ }
+
+ public function findSoftware(string $name): ?Software
+ {
+ return $this->registry[$name] ?? null;
}
/**
@@ -33,7 +36,7 @@ public function findSoftware(string $name): ?Software
*/
public function getIterator(): \Traversable
{
- yield from $this->softwareRegistry->software;
+ yield from $this->registry;
}
/**
@@ -41,6 +44,21 @@ public function getIterator(): \Traversable
*/
public function count(): int
{
- return \count($this->softwareRegistry->software);
+ return \count($this->registry);
+ }
+
+ private function loadDefaultRegistry(): void
+ {
+ $json = \json_decode(
+ \file_get_contents(Info::ROOT_DIR . '/resources/software.json'),
+ true,
+ 16,
+ JSON_THROW_ON_ERROR,
+ );
+
+ foreach ($json as $softwareArray) {
+ $software = Software::fromArray($softwareArray);
+ $this->registry[$software->getId()] ??= $software;
+ }
}
}
diff --git a/src/Module/Downloader/TaskManager.php b/src/Module/Downloader/TaskManager.php
index 6959eda..687e7ec 100644
--- a/src/Module/Downloader/TaskManager.php
+++ b/src/Module/Downloader/TaskManager.php
@@ -41,6 +41,7 @@ public function getProcessor(): \Generator
yield $task->resume();
} catch (\Throwable $e) {
+ $this->logger->error($e->getMessage());
$this->logger->exception($e);
unset($this->tasks[$key]);
yield $e;
@@ -55,7 +56,7 @@ public function await(): void
$processor = $this->getProcessor();
$processor->current();
while ($processor->valid()) {
- $processor->send(null);
+ $processor->next();
}
}
}
diff --git a/src/Module/Repository/Collection/ReleasesCollection.php b/src/Module/Repository/Collection/ReleasesCollection.php
index c5814a8..7191d88 100644
--- a/src/Module/Repository/Collection/ReleasesCollection.php
+++ b/src/Module/Repository/Collection/ReleasesCollection.php
@@ -16,33 +16,21 @@
final class ReleasesCollection extends Collection
{
/**
- * @param non-empty-string ...$constraints
+ * @param non-empty-string $constraint
* @return $this
*/
- public function satisfies(string ...$constraints): self
+ public function satisfies(string $constraint): self
{
- $result = $this;
-
- foreach ($this->constraints($constraints) as $constraint) {
- $result = $result->filter(static fn(ReleaseInterface $r): bool => $r->satisfies($constraint));
- }
-
- return $result;
+ return $this->filter(static fn(ReleaseInterface $r): bool => $r->satisfies($constraint));
}
/**
- * @param string ...$constraints
+ * @param non-empty-string $constraint
* @return $this
*/
- public function notSatisfies(string ...$constraints): self
+ public function notSatisfies(string $constraint): self
{
- $result = $this;
-
- foreach ($this->constraints($constraints) as $constraint) {
- $result = $result->except(static fn(ReleaseInterface $r): bool => $r->satisfies($constraint));
- }
-
- return $result;
+ return $this->except(static fn(ReleaseInterface $r): bool => $r->satisfies($constraint));
}
/**
@@ -99,23 +87,6 @@ public function minimumStability(Stability $stability): self
);
}
- /**
- * @param array $constraints
- * @return array
- */
- private function constraints(array $constraints): array
- {
- $result = [];
-
- foreach ($constraints as $constraint) {
- foreach (\explode('|', $constraint) as $expression) {
- $result[] = $expression;
- }
- }
-
- return \array_unique(\array_filter(\array_map('\\trim', $result)));
- }
-
/**
* @return non-empty-string
*/
diff --git a/src/Module/Repository/Internal/GitHub/Factory.php b/src/Module/Repository/Internal/GitHub/Factory.php
index 712426e..3a9ea3e 100644
--- a/src/Module/Repository/Internal/GitHub/Factory.php
+++ b/src/Module/Repository/Internal/GitHub/Factory.php
@@ -4,7 +4,7 @@
namespace Internal\DLoad\Module\Repository\Internal\GitHub;
-use Internal\DLoad\Module\Common\Config\GitHubConfig;
+use Internal\DLoad\Module\Common\Config\GitHub as GitHubConfig;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Contracts\HttpClient\HttpClientInterface;
diff --git a/src/Service/Logger.php b/src/Service/Logger.php
index 8fc50e3..6dd0a79 100644
--- a/src/Service/Logger.php
+++ b/src/Service/Logger.php
@@ -36,7 +36,7 @@ public function status(string $sender, string $message, string|int|float|bool ..
public function info(string $message, string|int|float|bool ...$values): void
{
- $this->echo("\033[32m" . \sprintf($message, ...$values) . "\033[0m\n", !$this->verbose);
+ $this->echo("\033[32m" . \sprintf($message, ...$values) . "\033[0m\n", false);
}
public function debug(string $message, string|int|float|bool ...$values): void