Skip to content

Commit

Permalink
feat: support custom XML config (./dload.xml by default); added cli…
Browse files Browse the repository at this point in the history
… option `config`
  • Loading branch information
roxblnfk committed Jul 19, 2024
1 parent 82b3261 commit aede340
Show file tree
Hide file tree
Showing 11 changed files with 191 additions and 24 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,25 @@ Download all the software from the preset:
```bash
./vendor/bin/dload get
```

### Custom software registry

```xml
<?xml version="1.0"?>
<dload>
<registry>
<software name="RoadRunner"
alias="rr"
description="High performant Application server"
>
<repository type="github"
uri="roadrunner-server/roadrunner"
asset-pattern="/^roadrunner-.*/"
/>
<file pattern="/^(roadrunner|rr)(?:\.exe)?$/"
rename="rr"
/>
</software>
</registry>
</dload>
```
11 changes: 2 additions & 9 deletions dload.xml
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
<?xml version="1.0"?>
<!-- Example -->
<dload>
<registry>
<registry overwrite="false">
<software name="RoadRunner" alias="rr" description="High performant Application server">
<repository type="github" uri="roadrunner-server/roadrunner" asset-pattern="/^roadrunner-.*/"/>
<file rename="rr" pattern="/^(roadrunner|rr)(?:\.exe)?$/" />
</software>
<software name="Dolt" description="Dolt is a SQL database that you can fork, clone, branch, merge, push and pull just like a Git repository">
<repository type="github" uri="dolthub/dolt" asset-pattern="/^dolt-.*/"/>
<file pattern="/^(dolt)(?:\.exe)?$/" />
</software>
<software name="Temporal" description="Temporal command-line interface and development server">
<repository type="github" uri="temporalio/cli" asset-pattern="/^temporal_cli_.*/"/>
<file pattern="/^(temporal)(?:\.exe)?$/" />
</software>
</registry>
</dload>
58 changes: 58 additions & 0 deletions resources/software.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
[
{
"name": "RoadRunner",
"alias": "rr",
"homepage": "https://roadrunner.dev",
"description": "High-performance PHP application server, load-balancer and process manager written in Golang",
"repositories": [
{
"type": "github",
"uri": "roadrunner-server/roadrunner",
"asset-pattern": "/^roadrunner-.*/"
}
],
"files": [
{
"pattern": "/^(roadrunner|rr)(?:\\.exe)?$/",
"rename": "rr"
}
]
},
{
"name": "Temporal",
"alias": "temporal",
"description": "Temporal SDK",
"homepage": "https://temporal.io",
"repositories": [
{
"type": "github",
"uri": "temporalio/sdk-php",
"asset-pattern": "/^temporal-.*/"
}
],
"files": [
{
"pattern": "/^temporal-.*/",
"rename": "temporal"
}
]
},
{
"name": "Dolt",
"alias": "dolt",
"description": "Dolt is a SQL database that you can fork, clone, branch, merge, push and pull just like a git repository.",
"homepage": "https://www.dolthub.com",
"repositories": [
{
"type": "github",
"uri": "dolthub/dolt",
"asset-pattern": "/^dolt-.*/"
}
],
"files": [
{
"pattern": "/^dolt-.*/"
}
]
}
]
31 changes: 30 additions & 1 deletion src/Command/Base.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Internal\DLoad\Service\Logger;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\StyleInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
Expand All @@ -22,22 +23,50 @@ abstract class Base extends Command

protected Container $container;

public function configure(): void

Check warning on line 26 in src/Command/Base.php

View check run for this annotation

Codecov / codecov/patch

src/Command/Base.php#L26

Added line #L26 was not covered by tests
{
parent::configure();
$this->addOption('config', null, InputOption::VALUE_OPTIONAL, 'Path to the configuration file');

Check warning on line 29 in src/Command/Base.php

View check run for this annotation

Codecov / codecov/patch

src/Command/Base.php#L28-L29

Added lines #L28 - L29 were not covered by tests
}

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),

Check warning on line 38 in src/Command/Base.php

View check run for this annotation

Codecov / codecov/patch

src/Command/Base.php#L38

Added line #L38 was not covered by tests
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);
$container->set($this->logger);

return Command::SUCCESS;
}

/**
* @return non-empty-string|null Path to the configuration file

Check failure on line 53 in src/Command/Base.php

View workflow job for this annotation

GitHub Actions / psalm (ubuntu-latest, 8.2, locked)

MoreSpecificReturnType

src/Command/Base.php:53:16: MoreSpecificReturnType: The declared return type 'non-empty-string|null' for Internal\DLoad\Command\Base::getConfigFile is more specific than the inferred return type 'null|string' (see https://psalm.dev/070)

Check failure on line 53 in src/Command/Base.php

View workflow job for this annotation

GitHub Actions / psalm (ubuntu-latest, 8.2, locked)

MoreSpecificReturnType

src/Command/Base.php:53:16: MoreSpecificReturnType: The declared return type 'non-empty-string|null' for Internal\DLoad\Command\Base::getConfigFile is more specific than the inferred return type 'null|string' (see https://psalm.dev/070)
*/
private function getConfigFile(InputInterface $input): ?string

Check warning on line 55 in src/Command/Base.php

View check run for this annotation

Codecov / codecov/patch

src/Command/Base.php#L55

Added line #L55 was not covered by tests
{
/** @var string|null $config */
$config = $input->getOption('config');
$isConfigured = $config !== null;
$config ??= './dload.xml';

Check warning on line 60 in src/Command/Base.php

View check run for this annotation

Codecov / codecov/patch

src/Command/Base.php#L58-L60

Added lines #L58 - L60 were not covered by tests

if (\is_file($config)) {
return $config;

Check failure on line 63 in src/Command/Base.php

View workflow job for this annotation

GitHub Actions / psalm (ubuntu-latest, 8.2, locked)

LessSpecificReturnStatement

src/Command/Base.php:63:20: LessSpecificReturnStatement: The type 'string' is more general than the declared return type 'non-empty-string|null' for Internal\DLoad\Command\Base::getConfigFile (see https://psalm.dev/129)

Check failure on line 63 in src/Command/Base.php

View workflow job for this annotation

GitHub Actions / psalm (ubuntu-latest, 8.2, locked)

LessSpecificReturnStatement

src/Command/Base.php:63:20: LessSpecificReturnStatement: The type 'string' is more general than the declared return type 'non-empty-string|null' for Internal\DLoad\Command\Base::getConfigFile (see https://psalm.dev/129)

Check warning on line 63 in src/Command/Base.php

View check run for this annotation

Codecov / codecov/patch

src/Command/Base.php#L62-L63

Added lines #L62 - L63 were not covered by tests
}

$isConfigured and throw new \InvalidArgumentException(
'Configuration file not found: ' . $config,

Check warning on line 67 in src/Command/Base.php

View check run for this annotation

Codecov / codecov/patch

src/Command/Base.php#L66-L67

Added lines #L66 - L67 were not covered by tests
);

return null;

Check warning on line 70 in src/Command/Base.php

View check run for this annotation

Codecov / codecov/patch

src/Command/Base.php#L70

Added line #L70 was not covered by tests
}
}
1 change: 1 addition & 0 deletions src/Command/Get.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ final class Get extends Base implements SignalableCommandInterface
{
public function configure(): void
{
parent::configure();

Check warning on line 30 in src/Command/Get.php

View check run for this annotation

Codecov / codecov/patch

src/Command/Get.php#L30

Added line #L30 was not covered by tests
$this->addArgument('binary', InputArgument::REQUIRED, 'Binary 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.');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@

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[]
*/
Expand Down
9 changes: 9 additions & 0 deletions src/Module/Common/Config/Embed/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,13 @@ final class File

#[XPath('@pattern')]
public string $pattern = '/^.*$/';

public static function fromArray(mixed $fileArray): self

Check warning on line 20 in src/Module/Common/Config/Embed/File.php

View check run for this annotation

Codecov / codecov/patch

src/Module/Common/Config/Embed/File.php#L20

Added line #L20 was not covered by tests
{
$self = new self();
$self->rename = $fileArray['rename'] ?? null;

Check failure on line 23 in src/Module/Common/Config/Embed/File.php

View workflow job for this annotation

GitHub Actions / psalm (ubuntu-latest, 8.2, locked)

MixedAssignment

src/Module/Common/Config/Embed/File.php:23:9: MixedAssignment: Unable to determine the type that $self->rename is being assigned to (see https://psalm.dev/032)

Check failure on line 23 in src/Module/Common/Config/Embed/File.php

View workflow job for this annotation

GitHub Actions / psalm (ubuntu-latest, 8.2, locked)

MixedArrayAccess

src/Module/Common/Config/Embed/File.php:23:25: MixedArrayAccess: Cannot access array value on mixed variable $fileArray (see https://psalm.dev/051)

Check failure on line 23 in src/Module/Common/Config/Embed/File.php

View workflow job for this annotation

GitHub Actions / psalm (ubuntu-latest, 8.2, locked)

MixedAssignment

src/Module/Common/Config/Embed/File.php:23:9: MixedAssignment: Unable to determine the type that $self->rename is being assigned to (see https://psalm.dev/032)

Check failure on line 23 in src/Module/Common/Config/Embed/File.php

View workflow job for this annotation

GitHub Actions / psalm (ubuntu-latest, 8.2, locked)

MixedArrayAccess

src/Module/Common/Config/Embed/File.php:23:25: MixedArrayAccess: Cannot access array value on mixed variable $fileArray (see https://psalm.dev/051)
$self->pattern = $fileArray['pattern'] ?? '/^.*$/';

Check failure on line 24 in src/Module/Common/Config/Embed/File.php

View workflow job for this annotation

GitHub Actions / psalm (ubuntu-latest, 8.2, locked)

MixedAssignment

src/Module/Common/Config/Embed/File.php:24:9: MixedAssignment: Unable to determine the type that $self->pattern is being assigned to (see https://psalm.dev/032)

Check failure on line 24 in src/Module/Common/Config/Embed/File.php

View workflow job for this annotation

GitHub Actions / psalm (ubuntu-latest, 8.2, locked)

MixedAssignment

src/Module/Common/Config/Embed/File.php:24:9: MixedAssignment: Unable to determine the type that $self->pattern is being assigned to (see https://psalm.dev/032)

Check warning on line 24 in src/Module/Common/Config/Embed/File.php

View check run for this annotation

Codecov / codecov/patch

src/Module/Common/Config/Embed/File.php#L22-L24

Added lines #L22 - L24 were not covered by tests

return $self;

Check warning on line 26 in src/Module/Common/Config/Embed/File.php

View check run for this annotation

Codecov / codecov/patch

src/Module/Common/Config/Embed/File.php#L26

Added line #L26 was not covered by tests
}
}
10 changes: 10 additions & 0 deletions src/Module/Common/Config/Embed/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,14 @@ final class Repository

#[XPath('@asset-pattern')]
public string $assetPattern = '/^.*$/';

public static function fromArray(mixed $repositoryArray): self

Check warning on line 20 in src/Module/Common/Config/Embed/Repository.php

View check run for this annotation

Codecov / codecov/patch

src/Module/Common/Config/Embed/Repository.php#L20

Added line #L20 was not covered by tests
{
$self = new self();
$self->type = $repositoryArray['type'] ?? 'github';
$self->uri = $repositoryArray['uri'];
$self->assetPattern = $repositoryArray['asset-pattern'] ?? '/^.*$/';

Check warning on line 25 in src/Module/Common/Config/Embed/Repository.php

View check run for this annotation

Codecov / codecov/patch

src/Module/Common/Config/Embed/Repository.php#L22-L25

Added lines #L22 - L25 were not covered by tests

return $self;

Check warning on line 27 in src/Module/Common/Config/Embed/Repository.php

View check run for this annotation

Codecov / codecov/patch

src/Module/Common/Config/Embed/Repository.php#L27

Added line #L27 was not covered by tests
}
}
18 changes: 18 additions & 0 deletions src/Module/Common/Config/Embed/Software.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,24 @@ final class Software
#[XPathEmbedList('file', File::class)]
public array $files = [];

public static function fromArray(mixed $softwareArray): self

Check warning on line 36 in src/Module/Common/Config/Embed/Software.php

View check run for this annotation

Codecov / codecov/patch

src/Module/Common/Config/Embed/Software.php#L36

Added line #L36 was not covered by tests
{
$self = new self();
$self->name = $softwareArray['name'];
$self->alias = $softwareArray['alias'] ?? null;
$self->description = $softwareArray['description'] ?? '';
$self->repositories = \array_map(
static fn(array $repositoryArray) => Repository::fromArray($repositoryArray),
$softwareArray['repository'] ?? [],

Check warning on line 44 in src/Module/Common/Config/Embed/Software.php

View check run for this annotation

Codecov / codecov/patch

src/Module/Common/Config/Embed/Software.php#L38-L44

Added lines #L38 - L44 were not covered by tests
);
$self->files = \array_map(
static fn(array $fileArray) => File::fromArray($fileArray),
$softwareArray['file'] ?? [],

Check warning on line 48 in src/Module/Common/Config/Embed/Software.php

View check run for this annotation

Codecov / codecov/patch

src/Module/Common/Config/Embed/Software.php#L46-L48

Added lines #L46 - L48 were not covered by tests
);

return $self;

Check warning on line 51 in src/Module/Common/Config/Embed/Software.php

View check run for this annotation

Codecov / codecov/patch

src/Module/Common/Config/Embed/Software.php#L51

Added line #L51 was not covered by tests
}

/**
* @return non-empty-string
*/
Expand Down
6 changes: 5 additions & 1 deletion src/Module/Common/Internal/Injection/ConfigLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,12 @@ private function getXPath(XPath $attribute): mixed

private function getXPathEmbeddedList(XPathEmbedList $attribute): array
{
if ($this->xml === null) {
return [];

Check warning on line 130 in src/Module/Common/Internal/Injection/ConfigLoader.php

View check run for this annotation

Codecov / codecov/patch

src/Module/Common/Internal/Injection/ConfigLoader.php#L129-L130

Added lines #L129 - L130 were not covered by tests
}

$result = [];
$value = $this->xml?->xpath($attribute->path);
$value = $this->xml->xpath($attribute->path);

Check warning on line 134 in src/Module/Common/Internal/Injection/ConfigLoader.php

View check run for this annotation

Codecov / codecov/patch

src/Module/Common/Internal/Injection/ConfigLoader.php#L134

Added line #L134 was not covered by tests
\is_array($value) or throw new \Exception(\sprintf('Invalid XPath `%s`', $attribute->path));

foreach ($value as $xml) {
Expand Down
43 changes: 31 additions & 12 deletions src/Module/Downloader/SoftwareCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,62 @@

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;

/**
* @implements IteratorAggregate<Software>
*/
final class SoftwareCollection implements \IteratorAggregate, \Countable
{
/** @var array<non-empty-string, Software> */
private array $registry = [];

public function __construct(
private readonly SoftwareRegistry $softwareRegistry,
) {}
CustomSoftwareRegistry $softwareRegistry,
) {
foreach ($softwareRegistry->software as $software) {
$this->registry[$software->getId()] = $software;

Check warning on line 24 in src/Module/Downloader/SoftwareCollection.php

View check run for this annotation

Codecov / codecov/patch

src/Module/Downloader/SoftwareCollection.php#L23-L24

Added lines #L23 - L24 were not covered by tests
}

$softwareRegistry->overwrite or $this->loadDefaultRegistry();

Check warning on line 27 in src/Module/Downloader/SoftwareCollection.php

View check run for this annotation

Codecov / codecov/patch

src/Module/Downloader/SoftwareCollection.php#L27

Added line #L27 was not covered by tests
}

public function findSoftware(string $name): ?Software
{
foreach ($this->softwareRegistry->software as $software) {
if ($software->getId() === $name) {
return $software;
}
}

return null;
return $this->registry[$name] ?? null;

Check warning on line 32 in src/Module/Downloader/SoftwareCollection.php

View check run for this annotation

Codecov / codecov/patch

src/Module/Downloader/SoftwareCollection.php#L32

Added line #L32 was not covered by tests
}

/**
* @return \Traversable<Software>
*/
public function getIterator(): \Traversable
{
yield from $this->softwareRegistry->software;
yield from $this->registry;

Check warning on line 40 in src/Module/Downloader/SoftwareCollection.php

View check run for this annotation

Codecov / codecov/patch

src/Module/Downloader/SoftwareCollection.php#L40

Added line #L40 was not covered by tests
}

/**
* @return int<0, max>
*/
public function count(): int
{
return \count($this->softwareRegistry->software);
return \count($this->registry);

Check warning on line 48 in src/Module/Downloader/SoftwareCollection.php

View check run for this annotation

Codecov / codecov/patch

src/Module/Downloader/SoftwareCollection.php#L48

Added line #L48 was not covered by tests
}

private function loadDefaultRegistry(): void

Check warning on line 51 in src/Module/Downloader/SoftwareCollection.php

View check run for this annotation

Codecov / codecov/patch

src/Module/Downloader/SoftwareCollection.php#L51

Added line #L51 was not covered by tests
{
$json = \json_decode(
\file_get_contents(Info::ROOT_DIR . '/resources/software.json'),
true,
16,
JSON_THROW_ON_ERROR,

Check warning on line 57 in src/Module/Downloader/SoftwareCollection.php

View check run for this annotation

Codecov / codecov/patch

src/Module/Downloader/SoftwareCollection.php#L53-L57

Added lines #L53 - L57 were not covered by tests
);

foreach ($json as $softwareArray) {
$software = Software::fromArray($softwareArray);
$this->registry[$software->getId()] ??= $software;

Check warning on line 62 in src/Module/Downloader/SoftwareCollection.php

View check run for this annotation

Codecov / codecov/patch

src/Module/Downloader/SoftwareCollection.php#L60-L62

Added lines #L60 - L62 were not covered by tests
}
}
}

0 comments on commit aede340

Please sign in to comment.