Skip to content

Commit

Permalink
Merge pull request #19 from jakzal/feature/tag-filter
Browse files Browse the repository at this point in the history
Tag filtering
  • Loading branch information
jakzal authored Dec 31, 2018
2 parents 37f1a0e + 2c60e6f commit f026ca4
Show file tree
Hide file tree
Showing 21 changed files with 352 additions and 58 deletions.
38 changes: 37 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,15 @@ curl -s https://api.github.com/repos/jakzal/toolbox/releases/latest \
./toolbox list-tools
```

#### Filter tools by tags

To exclude some tools from the listing multiple `--exclude-tag` options can be added.
The `--tag` option can be used to filter tools by tags.

```
bin/toolbox.php list-tools --exclude-tag exclude-php:7.3 --exclude-tag foo --tag bar
```

### Install tools

```
Expand Down Expand Up @@ -117,6 +126,15 @@ To only see what commands would be executed, use the dry run mode:
./toolbox install --dry-run
```

#### Filter tools by tags

To exclude some tools from the installation multiple `--exclude-tag` options can be added.
The `--tag` option can be used to filter tools by tags.

```
bin/toolbox.php install --exclude-tag exclude-php:7.3 --exclude-tag foo --tag bar
```

### Test if installed tools are usable

```
Expand All @@ -131,6 +149,15 @@ To only see what commands would be executed, use the dry run mode:
./toolbox test --dry-run
```

#### Filter tools by tags

To exclude some tools from the generated test command multiple `--exclude-tag` options can be added.
The `--tag` option can be used to filter tools by tags.

```
bin/toolbox.php test --exclude-tag exclude-php:7.3 --exclude-tag foo --tag bar
```

### Tools definitions

By default `resources/pre-installation.json` and `resources/tools.json` are used to load tool definitions.
Expand All @@ -144,4 +171,13 @@ Tool definition location(s) can be also specified with the `TOOLBOX_JSON` enviro

```
TOOLBOX_JSON='path/to/file1.json,path/to/file2.json' ./toolbox list-tools
```
```

### Tool tags

Tools can be tagged in order to enable grouping and filtering them.

The tags below have a special meaning:

* `pre-installation` - these tools will be installed before any other tools.
* `exclude-php:7.3`, `exclude-php:7.1` etc - used to exclude installation on the specified php version.
3 changes: 2 additions & 1 deletion bin/devkit.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Zalas\Toolbox\Json\JsonTools;
use Zalas\Toolbox\Tool\Command;
use Zalas\Toolbox\Tool\Command\ShCommand;
use Zalas\Toolbox\Tool\Filter;
use Zalas\Toolbox\Tool\Tool;

$application = new Application('Toolbox DevKit', 'dev');
Expand All @@ -30,7 +31,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
{
$jsonPath = $input->getOption('tools');
$readmePath = $input->getOption('readme');
$tools = (new JsonTools(function () use ($jsonPath) { return $jsonPath; }))->all();
$tools = (new JsonTools(function () use ($jsonPath) { return $jsonPath; }))->all(new Filter([], []));
$toolsList = $tools->reduce('', function ($acc, Tool $tool) {
return $acc . sprintf('* %s - [%s](%s)', $tool->name(), $tool->summary(), $tool->website()) . PHP_EOL;
});
Expand Down
9 changes: 5 additions & 4 deletions resources/tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,11 @@
"website": "https://infection.github.io/",
"command": {
"file-download": {
"url": "https://github.com/infection/infection/releases/download/0.10.3/infection.phar.asc",
"url": "https://github.com/infection/infection/releases/download/0.11.4/infection.phar.asc",
"file": "%target-dir%/infection.phar.asc"
},
"phar-download": {
"phar": "https://github.com/infection/infection/releases/download/0.10.3/infection.phar",
"phar": "https://github.com/infection/infection/releases/download/0.11.4/infection.phar",
"bin": "%target-dir%/infection"
}
},
Expand Down Expand Up @@ -156,7 +156,7 @@
"website": "https://github.com/phan/phan",
"command": {
"phar-download": {
"phar": "https://github.com/phan/phan/releases/download/1.0.1/phan.phar",
"phar": "https://github.com/phan/phan/releases/download/1.1.10/phan.phar",
"bin": "%target-dir%/phan"
}
},
Expand Down Expand Up @@ -184,7 +184,8 @@
"bin": "%target-dir%/php-cs-fixer"
}
},
"test": "php-cs-fixer list"
"test": "php-cs-fixer list",
"tags": ["exclude-php:7.3"]
},
{
"name": "php-formatter",
Expand Down
5 changes: 4 additions & 1 deletion src/Cli/Command/InstallCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Zalas\Toolbox\Runner\Runner;
use Zalas\Toolbox\Tool\Filter;
use Zalas\Toolbox\UseCase\InstallTools;

final class InstallCommand extends Command
Expand All @@ -31,10 +32,12 @@ protected function configure()
$this->setDescription('Installs tools');
$this->addOption('dry-run', null, InputOption::VALUE_NONE, 'Output the command without executing it');
$this->addOption('target-dir', null, InputOption::VALUE_REQUIRED, 'The target installation directory', $this->defaultTargetDir());
$this->addOption('exclude-tag', 'e', InputOption::VALUE_REQUIRED|InputOption::VALUE_IS_ARRAY, 'Tool tags to exclude');
$this->addOption('tag', 't', InputOption::VALUE_REQUIRED|InputOption::VALUE_IS_ARRAY, 'Tool tags to filter by');
}

protected function execute(InputInterface $input, OutputInterface $output)
{
return $this->runner->run(\call_user_func($this->useCase));
return $this->runner->run(\call_user_func($this->useCase, new Filter($input->getOption('exclude-tag'), $input->getOption('tag'))));
}
}
6 changes: 5 additions & 1 deletion src/Cli/Command/ListCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@

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;
use Zalas\Toolbox\Tool\Filter;
use Zalas\Toolbox\Tool\Tool;
use Zalas\Toolbox\UseCase\ListTools;

Expand All @@ -26,11 +28,13 @@ public function __construct(ListTools $listTools)
protected function configure()
{
$this->setDescription('Lists available tools');
$this->addOption('exclude-tag', 'e', InputOption::VALUE_REQUIRED|InputOption::VALUE_IS_ARRAY, 'Tool tags to exclude');
$this->addOption('tag', 't', InputOption::VALUE_REQUIRED|InputOption::VALUE_IS_ARRAY, 'Tool tags to filter by');
}

protected function execute(InputInterface $input, OutputInterface $output)
{
$tools = \call_user_func($this->listTools);
$tools = \call_user_func($this->listTools, new Filter($input->getOption('exclude-tag'), $input->getOption('tag')));

$style = $this->createStyle($input, $output);
$style->title('Available tools');
Expand Down
5 changes: 4 additions & 1 deletion src/Cli/Command/TestCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Zalas\Toolbox\Runner\Runner;
use Zalas\Toolbox\Tool\Filter;
use Zalas\Toolbox\UseCase\TestTools;

final class TestCommand extends Command
Expand All @@ -31,10 +32,12 @@ protected function configure()
$this->setDescription('Runs basic tests to verify tools are installed');
$this->addOption('dry-run', null, InputOption::VALUE_NONE, 'Output the command without executing it');
$this->addOption('target-dir', null, InputOption::VALUE_REQUIRED, 'The target installation directory', $this->defaultTargetDir());
$this->addOption('exclude-tag', 'e', InputOption::VALUE_REQUIRED|InputOption::VALUE_IS_ARRAY, 'Tool tags to exclude');
$this->addOption('tag', 't', InputOption::VALUE_REQUIRED|InputOption::VALUE_IS_ARRAY, 'Tool tags to filter by');
}

protected function execute(InputInterface $input, OutputInterface $output)
{
return $this->runner->run(\call_user_func($this->useCase));
return $this->runner->run(\call_user_func($this->useCase, new Filter($input->getOption('exclude-tag'), $input->getOption('tag'))));
}
}
8 changes: 7 additions & 1 deletion src/Json/JsonTools.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use RuntimeException;
use Zalas\Toolbox\Json\Factory\ToolFactory;
use Zalas\Toolbox\Tool\Collection;
use Zalas\Toolbox\Tool\Filter;
use Zalas\Toolbox\Tool\Tool;
use Zalas\Toolbox\Tool\Tools;

Expand All @@ -24,7 +25,12 @@ public function __construct(callable $resourceLocator)
/**
* @return Collection|Tool[]
*/
public function all(): Collection
public function all(Filter $filter): Collection
{
return $this->loadTools()->filter($filter);
}

private function loadTools(): Collection
{
return \array_reduce($this->resources(), function (Collection $tools, string $resource): Collection {
return $tools->merge(Collection::create(
Expand Down
32 changes: 32 additions & 0 deletions src/Tool/Filter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php declare(strict_types=1);

namespace Zalas\Toolbox\Tool;

class Filter
{
/**
* @var string[]
*/
private $excludedTags;

/**
* @var string[]
*/
private $tags;

/**
* @param string[] $excludedTags
* @param string[] $tags
*/
public function __construct(array $excludedTags, array $tags)
{
$this->excludedTags = $excludedTags;
$this->tags = $tags;
}

public function __invoke(Tool $tool): bool
{
return $this->excludedTags === \array_diff($this->excludedTags, $tool->tags())
&& (empty($this->tags) || \array_intersect($this->tags, $tool->tags()));
}
}
2 changes: 1 addition & 1 deletion src/Tool/Tools.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ interface Tools
* @return Collection|Tool[]
* @throws RuntimeException in case tools cannot be loaded
*/
public function all(): Collection;
public function all(Filter $filter): Collection;
}
5 changes: 3 additions & 2 deletions src/UseCase/InstallTools.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Zalas\Toolbox\Tool\Command\OptimisedComposerBinPluginCommand;
use Zalas\Toolbox\Tool\Command\PharDownloadCommand;
use Zalas\Toolbox\Tool\Command\ShCommand;
use Zalas\Toolbox\Tool\Filter;
use Zalas\Toolbox\Tool\Tool;
use Zalas\Toolbox\Tool\Tools;

Expand All @@ -28,9 +29,9 @@ public function __construct(Tools $tools)
$this->tools = $tools;
}

public function __invoke(): Command
public function __invoke(Filter $filter): Command
{
$tools = $this->tools->all();
$tools = $this->tools->all($filter);
$installationCommands = $this->installationCommands($tools);
$commandFilter = $this->commandFilter($this->toolCommands($tools));

Expand Down
5 changes: 3 additions & 2 deletions src/UseCase/ListTools.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Zalas\Toolbox\UseCase;

use Zalas\Toolbox\Tool\Collection;
use Zalas\Toolbox\Tool\Filter;
use Zalas\Toolbox\Tool\Tool;
use Zalas\Toolbox\Tool\Tools;

Expand All @@ -21,8 +22,8 @@ public function __construct(Tools $tools)
/**
* @return Collection|Tool[]
*/
public function __invoke(): Collection
public function __invoke(Filter $filter): Collection
{
return $this->tools->all();
return $this->tools->all($filter);
}
}
5 changes: 3 additions & 2 deletions src/UseCase/TestTools.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Zalas\Toolbox\Tool\Command;
use Zalas\Toolbox\Tool\Command\MultiStepCommand;
use Zalas\Toolbox\Tool\Filter;
use Zalas\Toolbox\Tool\Tool;
use Zalas\Toolbox\Tool\Tools;

Expand All @@ -16,10 +17,10 @@ public function __construct(Tools $tools)
$this->tools = $tools;
}

public function __invoke(): Command
public function __invoke(Filter $filter): Command
{
return new MultiStepCommand(
$this->tools->all()->map(function (Tool $tool) {
$this->tools->all($filter)->map(function (Tool $tool) {
return $tool->testCommand();
})
);
Expand Down
25 changes: 23 additions & 2 deletions tests/Cli/Command/InstallCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Zalas\Toolbox\Cli\Command\InstallCommand;
use Zalas\Toolbox\Runner\Runner;
use Zalas\Toolbox\Tool\Command;
use Zalas\Toolbox\Tool\Filter;
use Zalas\Toolbox\UseCase\InstallTools;

class InstallCommandTest extends ToolboxCommandTestCase
Expand Down Expand Up @@ -34,7 +35,7 @@ protected function setUp()
public function test_it_runs_the_install_tools_use_case()
{
$command = $this->createCommand();
$this->useCase->__invoke()->willReturn($command);
$this->useCase->__invoke(Argument::type(Filter::class))->willReturn($command);
$this->runner->run($command)->willReturn(0);

$tester = $this->executeCliCommand();
Expand All @@ -46,14 +47,24 @@ public function test_it_runs_the_install_tools_use_case()

public function test_it_returns_the_status_code_of_the_run()
{
$this->useCase->__invoke()->willReturn($this->createCommand());
$this->useCase->__invoke(Argument::type(Filter::class))->willReturn($this->createCommand());
$this->runner->run(Argument::any())->willReturn(1);

$tester = $this->executeCliCommand();

$this->assertSame(1, $tester->getStatusCode());
}

public function test_it_filters_by_tags()
{
$this->useCase->__invoke(Argument::type(Filter::class))->willReturn($this->createCommand());
$this->runner->run(Argument::any())->willReturn(0);

$this->executeCliCommand(['--exclude-tag' => ['foo'], '--tag' => ['bar']]);

$this->useCase->__invoke(new Filter(['foo'], ['bar']))->shouldBeCalled();
}

public function test_it_defines_dry_run_option()
{
$this->assertTrue($this->cliCommand()->getDefinition()->hasOption('dry-run'));
Expand All @@ -73,6 +84,16 @@ public function test_it_takes_the_target_dir_option_default_from_environment_if_
$this->assertSame('/tmp', $this->cliCommand()->getDefinition()->getOption('target-dir')->getDefault());
}

public function test_it_defines_exclude_tag_option()
{
$this->assertTrue($this->cliCommand()->getDefinition()->hasOption('exclude-tag'));
}

public function test_it_defines_tag_option()
{
$this->assertTrue($this->cliCommand()->getDefinition()->hasOption('tag'));
}

protected function getContainerTestDoubles(): array
{
return [
Expand Down
Loading

0 comments on commit f026ca4

Please sign in to comment.