From 21d4f773932428f186d86e2366728221f79178a2 Mon Sep 17 00:00:00 2001 From: Jakub Zalas Date: Sat, 23 Mar 2019 10:02:36 +0100 Subject: [PATCH 1/3] Bump versions of tools --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 1512c632..c30962aa 100644 --- a/Makefile +++ b/Makefile @@ -140,7 +140,7 @@ tools/php-cs-fixer: curl -Ls http://cs.sensiolabs.org/download/php-cs-fixer-v2.phar -o tools/php-cs-fixer && chmod +x tools/php-cs-fixer tools/deptrac: - curl -Ls https://github.com/sensiolabs-de/deptrac/releases/download/0.4.0/deptrac.phar -o tools/deptrac && chmod +x tools/deptrac + curl -Ls https://github.com/sensiolabs-de/deptrac/releases/download/0.5.0/deptrac.phar -o tools/deptrac && chmod +x tools/deptrac tools/infection: tools/infection.pubkey curl -Ls https://github.com/infection/infection/releases/download/0.10.6/infection.phar -o tools/infection && chmod +x tools/infection @@ -149,4 +149,4 @@ tools/infection.pubkey: curl -Ls https://github.com/infection/infection/releases/download/0.10.6/infection.phar.pubkey -o tools/infection.pubkey tools/box: - curl -Ls https://github.com/humbug/box/releases/download/3.4.0/box.phar -o tools/box && chmod +x tools/box + curl -Ls https://github.com/humbug/box/releases/download/3.6.0/box.phar -o tools/box && chmod +x tools/box From 618ee925023542f00f921129001e360b446389ed Mon Sep 17 00:00:00 2001 From: Jakub Zalas Date: Sat, 23 Mar 2019 11:46:04 +0100 Subject: [PATCH 2/3] Manually link to composer-bin-plugin installed commands Otherwise there is no guarantee which version of the library the link was created for, as there might be still multiple versions installed. The plugin will link to the first one that was installed. It might not be the one we expected. i.e. rector installed phpstan in version 0.11.3 as one of its dependencies. phpstan 0.11.4 was also directly requested to be installed in a different composer-bin-plugin namespace. Since rectors phpstan was installed first, the link was pointing to the 0.11.3 version instead of 0.11.4 we expected. As a consequence no phpstan plugins could be loaded. --- CONTRIBUTING.md | 5 +- resources/pre-installation.json | 5 +- resources/tools.json | 42 ++++++++----- .../ComposerBinPluginCommandFactory.php | 16 ++++- src/Tool/Command/ComposerBinPluginCommand.php | 21 ++++++- .../Command/ComposerBinPluginLinkCommand.php | 29 +++++++++ .../OptimisedComposerBinPluginCommand.php | 16 ++++- .../ComposerBinPluginCommandFactoryTest.php | 19 ++++++ .../Command/ComposerBinPluginCommandTest.php | 25 +++++++- .../ComposerBinPluginLinkCommandTest.php | 34 +++++++++++ .../OptimisedComposerBinPluginCommandTest.php | 60 +++++++++++++++++-- tests/UseCase/InstallToolsTest.php | 4 +- 12 files changed, 249 insertions(+), 27 deletions(-) create mode 100644 src/Tool/Command/ComposerBinPluginLinkCommand.php create mode 100644 tests/Tool/Command/ComposerBinPluginLinkCommandTest.php diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 00208291..f5cd54f6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -65,12 +65,15 @@ Thanks to the isolation we're less likely to run into problem with conflicting d "command": { "composer-bin-plugin": { "package": "behat/behat", - "namespace": "behat" + "namespace": "behat", + "links": {"/tools/behat": "behat"} } } } ``` +The `links` attribute is optional, but it's recommended for packages that provide commands. + #### phar-download Downloads a phar from the given URL and puts it into the specified location. diff --git a/resources/pre-installation.json b/resources/pre-installation.json index 08db6bb2..9f5a0480 100644 --- a/resources/pre-installation.json +++ b/resources/pre-installation.json @@ -19,6 +19,9 @@ "command": { "composer-global-install": { "package": "bamarni/composer-bin-plugin" + }, + "sh": { + "command": "composer global config extra.bamarni-bin.bin-links false" } }, "test": "composer global show bamarni/composer-bin-plugin", @@ -37,4 +40,4 @@ "tags": ["pre-installation"] } ] -} \ No newline at end of file +} diff --git a/resources/tools.json b/resources/tools.json index e4c4771f..6257a5f1 100644 --- a/resources/tools.json +++ b/resources/tools.json @@ -33,7 +33,8 @@ "command": { "composer-bin-plugin": { "package": "bmitch/churn-php", - "namespace": "tools" + "namespace": "tools", + "links": {"%target-dir%/churn": "churn"} } }, "test": "churn list", @@ -106,7 +107,8 @@ "command": { "composer-bin-plugin": { "package": "exussum12/coverage-checker", - "namespace": "tools" + "namespace": "tools", + "links": {"%target-dir%/diffFilter": "diffFilter"} } }, "test": "diffFilter -v" @@ -160,7 +162,8 @@ "command": { "composer-bin-plugin": { "package": "brianium/paratest", - "namespace": "tools" + "namespace": "tools", + "links": {"%target-dir%/paratest": "paratest"} } }, "test": "paratest --version" @@ -198,7 +201,8 @@ "command": { "composer-bin-plugin": { "package": "akeneo/php-coupling-detector", - "namespace": "php-coupling-detector" + "namespace": "php-coupling-detector", + "links": {"%target-dir%/php-coupling-detector": "php-coupling-detector"} } }, "test": "php-coupling-detector list" @@ -278,7 +282,8 @@ "command": { "composer-bin-plugin": { "package": "rskuipers/php-assumptions", - "namespace": "php-assumptions" + "namespace": "php-assumptions", + "links": {"%target-dir%/phpa": "phpa"} } }, "test": "phpa --version", @@ -291,7 +296,8 @@ "command": { "composer-bin-plugin": { "package": "wapmorgan/php-code-analyzer", - "namespace": "tools" + "namespace": "tools", + "links": {"%target-dir%/phpca": "phpca"} } }, "test": "phpca -h" @@ -327,7 +333,8 @@ "command": { "composer-bin-plugin": { "package": "wapmorgan/php-code-fixer", - "namespace": "tools" + "namespace": "tools", + "links": {"%target-dir%/phpcf": "phpcf"} } }, "test": "phpcf -h" @@ -405,7 +412,8 @@ "command": { "composer-bin-plugin": { "package": "overtrue/phplint", - "namespace": "tools" + "namespace": "tools", + "links": {"%target-dir%/phplint": "phplint"} } }, "test": "phplint -V" @@ -454,7 +462,8 @@ "command": { "composer-bin-plugin": { "package": "povils/phpmnd", - "namespace": "tools" + "namespace": "tools", + "links": {"%target-dir%/phpmnd": "phpmnd"} } }, "test": "phpmnd -V" @@ -479,7 +488,8 @@ "command": { "composer-bin-plugin": { "package": "phpstan/phpstan", - "namespace": "phpstan" + "namespace": "phpstan", + "links": {"%target-dir%/phpstan": "phpstan"} } }, "test": "phpstan --version", @@ -642,7 +652,8 @@ "command": { "composer-bin-plugin": { "package": "psecio/parse:dev-master", - "namespace": "psecio-parse" + "namespace": "psecio-parse", + "links": {"%target-dir%/psecio-parse": "psecio-parse"} } }, "test": "psecio-parse --version" @@ -654,7 +665,8 @@ "command": { "composer-bin-plugin": { "package": "rector/rector", - "namespace": "tools" + "namespace": "tools", + "links": {"%target-dir%/rector": "rector"} } }, "test": "rector --version" @@ -692,7 +704,8 @@ "command": { "composer-bin-plugin": { "package": "symfony/phpunit-bridge", - "namespace": "symfony" + "namespace": "symfony", + "links": {"%target-dir%/simple-phpunit": "simple-phpunit"} }, "sh": { "command": "simple-phpunit install" @@ -707,7 +720,8 @@ "command": { "composer-bin-plugin": { "package": "edsonmedina/php_testability:dev-master", - "namespace": "tools" + "namespace": "tools", + "links": {"%target-dir%/testability": "testability"} } }, "test": "testability --help" diff --git a/src/Json/Factory/ComposerBinPluginCommandFactory.php b/src/Json/Factory/ComposerBinPluginCommandFactory.php index 54b2ef61..32d5312f 100644 --- a/src/Json/Factory/ComposerBinPluginCommandFactory.php +++ b/src/Json/Factory/ComposerBinPluginCommandFactory.php @@ -2,8 +2,10 @@ namespace Zalas\Toolbox\Json\Factory; +use Zalas\Toolbox\Tool\Collection; use Zalas\Toolbox\Tool\Command; use Zalas\Toolbox\Tool\Command\ComposerBinPluginCommand; +use Zalas\Toolbox\Tool\Command\ComposerBinPluginLinkCommand; final class ComposerBinPluginCommandFactory { @@ -11,6 +13,18 @@ public static function import(array $command): Command { Assert::requireFields(['package', 'namespace'], $command, 'ComposerBinPluginCommand'); - return new ComposerBinPluginCommand($command['package'], $command['namespace']); + return new ComposerBinPluginCommand($command['package'], $command['namespace'], self::importLinks($command)); + } + + private static function importLinks(array $command): Collection + { + $links = $command['links'] ?? []; + $namespace = $command['namespace']; + + return Collection::create( + \array_map(function (string $source, string $target) use ($namespace) { + return new ComposerBinPluginLinkCommand($source, $target, $namespace); + }, \array_values($links), \array_keys($links)) + ); } } diff --git a/src/Tool/Command/ComposerBinPluginCommand.php b/src/Tool/Command/ComposerBinPluginCommand.php index ca45def5..01145199 100644 --- a/src/Tool/Command/ComposerBinPluginCommand.php +++ b/src/Tool/Command/ComposerBinPluginCommand.php @@ -2,22 +2,27 @@ namespace Zalas\Toolbox\Tool\Command; +use Zalas\Toolbox\Tool\Collection; use Zalas\Toolbox\Tool\Command; final class ComposerBinPluginCommand implements Command { private $package; + private $namespace; - public function __construct(string $package, string $namespace) + private $links; + + public function __construct(string $package, string $namespace, Collection $links) { $this->package = $package; $this->namespace = $namespace; + $this->links = $links; } public function __toString(): string { - return \sprintf('composer global bin %s require --no-suggest --prefer-dist --update-no-dev -n %s', $this->namespace, $this->package); + return \sprintf('composer global bin %s require --no-suggest --prefer-dist --update-no-dev -n %s%s', $this->namespace, $this->package, $this->linkCommand()); } public function package(): string @@ -29,4 +34,16 @@ public function namespace(): string { return $this->namespace; } + + public function links(): Collection + { + return $this->links; + } + + private function linkCommand(): string + { + return $this->links->reduce('', function (string $command, ComposerBinPluginLinkCommand $link) { + return $command.' && '.$link; + }); + } } diff --git a/src/Tool/Command/ComposerBinPluginLinkCommand.php b/src/Tool/Command/ComposerBinPluginLinkCommand.php new file mode 100644 index 00000000..cb2cd99e --- /dev/null +++ b/src/Tool/Command/ComposerBinPluginLinkCommand.php @@ -0,0 +1,29 @@ +source = $source; + $this->target = $target; + $this->namespace = $namespace; + } + + public function __toString(): string + { + return \sprintf( + 'ln -sf ${COMPOSER_HOME:-"~/.composer"}/vendor-bin/%s/vendor/bin/%s %s', + $this->namespace, + $this->source, + $this->target + ); + } +} diff --git a/src/Tool/Command/OptimisedComposerBinPluginCommand.php b/src/Tool/Command/OptimisedComposerBinPluginCommand.php index 40c4d1a4..29ca3011 100644 --- a/src/Tool/Command/OptimisedComposerBinPluginCommand.php +++ b/src/Tool/Command/OptimisedComposerBinPluginCommand.php @@ -23,7 +23,7 @@ public function __construct(Collection $commands) public function __toString(): string { - return \implode(' && ', $this->commandsToRun($this->packagesGroupedByNamespace())); + return \implode(' && ', \array_merge($this->commandsToRun($this->packagesGroupedByNamespace()), $this->linksToCreate())); } private function packagesGroupedByNamespace(): array @@ -44,4 +44,18 @@ private function commandsToRun(array $packagesGrouped): array { return \array_map([$this, 'commandToRun'], \array_keys($packagesGrouped), $packagesGrouped); } + + private function linksToCreate(): array + { + return $this->commands + ->filter(function (ComposerBinPluginCommand $command) { + return !$command->links()->empty(); + }) + ->map(function (ComposerBinPluginCommand $command) { + return $command->links()->reduce('', function (string $command, ComposerBinPluginLinkCommand $link) { + return !empty($command) ? $command.' && '.$link : $link; + }); + }) + ->toArray(); + } } diff --git a/tests/Json/Factory/ComposerBinPluginCommandFactoryTest.php b/tests/Json/Factory/ComposerBinPluginCommandFactoryTest.php index 221434fb..4d9d52b2 100644 --- a/tests/Json/Factory/ComposerBinPluginCommandFactoryTest.php +++ b/tests/Json/Factory/ComposerBinPluginCommandFactoryTest.php @@ -4,7 +4,9 @@ use PHPUnit\Framework\TestCase; use Zalas\Toolbox\Json\Factory\ComposerBinPluginCommandFactory; +use Zalas\Toolbox\Tool\Collection; use Zalas\Toolbox\Tool\Command\ComposerBinPluginCommand; +use Zalas\Toolbox\Tool\Command\ComposerBinPluginLinkCommand; class ComposerBinPluginCommandFactoryTest extends TestCase { @@ -21,6 +23,23 @@ public function test_it_creates_a_command() $this->assertInstanceOf(ComposerBinPluginCommand::class, $command); } + public function test_it_creates_a_command_with_links_in_tools() + { + $command = ComposerBinPluginCommandFactory::import([ + 'package' => self::PACKAGE, + 'namespace' => self::NAMESPACE, + 'links' => ['/tools/phpstan' => 'phpstan'], + ]); + + $this->assertInstanceOf(ComposerBinPluginCommand::class, $command); + $this->assertEquals( + Collection::create([ + new ComposerBinPluginLinkCommand('phpstan', '/tools/phpstan', self::NAMESPACE) + ]), + $command->links() + ); + } + /** * @dataProvider provideRequiredProperties */ diff --git a/tests/Tool/Command/ComposerBinPluginCommandTest.php b/tests/Tool/Command/ComposerBinPluginCommandTest.php index 05d3fef1..b25ac947 100644 --- a/tests/Tool/Command/ComposerBinPluginCommandTest.php +++ b/tests/Tool/Command/ComposerBinPluginCommandTest.php @@ -3,8 +3,10 @@ namespace Zalas\Toolbox\Tests\Tool\Command; use PHPUnit\Framework\TestCase; +use Zalas\Toolbox\Tool\Collection; use Zalas\Toolbox\Tool\Command; use Zalas\Toolbox\Tool\Command\ComposerBinPluginCommand; +use Zalas\Toolbox\Tool\Command\ComposerBinPluginLinkCommand; class ComposerBinPluginCommandTest extends TestCase { @@ -18,7 +20,11 @@ class ComposerBinPluginCommandTest extends TestCase protected function setUp() { - $this->command = new ComposerBinPluginCommand(self::PACKAGE, self::NAMESPACE); + $this->command = new ComposerBinPluginCommand( + self::PACKAGE, + self::NAMESPACE, + Collection::create([]) + ); } public function test_it_is_a_command() @@ -36,4 +42,21 @@ public function test_it_exposes_the_package_and_namespace() $this->assertSame(self::PACKAGE, $this->command->package()); $this->assertSame(self::NAMESPACE, $this->command->namespace()); } + + public function test_it_optionally_creates_a_symlink() + { + $links = Collection::create([ + new ComposerBinPluginLinkCommand('phpstan', '/tools/phpstan', self::NAMESPACE) + ]); + $this->command = new ComposerBinPluginCommand(self::PACKAGE, self::NAMESPACE, $links); + + $this->assertSame($links, $this->command->links()); + $this->assertRegExp('#composer global bin tools require .*? phpstan/phpstan#', (string) $this->command); + $this->assertRegExp('# && ln -sf.*?phpstan /tools/phpstan#', (string) $this->command); + } + + public function test_it_does_not_create_a_symlink_if_links_option_was_not_given() + { + $this->assertNotRegExp('#ln -s#', (string) $this->command); + } } diff --git a/tests/Tool/Command/ComposerBinPluginLinkCommandTest.php b/tests/Tool/Command/ComposerBinPluginLinkCommandTest.php new file mode 100644 index 00000000..b50c2b83 --- /dev/null +++ b/tests/Tool/Command/ComposerBinPluginLinkCommandTest.php @@ -0,0 +1,34 @@ +command = new ComposerBinPluginLinkCommand(self::SOURCE, self::TARGET, self::NAMESPACE); + } + + public function test_it_is_a_command() + { + $this->assertInstanceOf(Command::class, $this->command); + } + + public function test_it_generates_a_symlink_command() + { + $this->assertRegExp('#ln -sf \$\{COMPOSER_HOME:-"~/.composer"\}/vendor-bin/tools/vendor/bin/churn /tools/churn#', (string) $this->command); + } +} diff --git a/tests/Tool/Command/OptimisedComposerBinPluginCommandTest.php b/tests/Tool/Command/OptimisedComposerBinPluginCommandTest.php index 1957f25b..705e4e4e 100644 --- a/tests/Tool/Command/OptimisedComposerBinPluginCommandTest.php +++ b/tests/Tool/Command/OptimisedComposerBinPluginCommandTest.php @@ -7,21 +7,22 @@ use Zalas\Toolbox\Tool\Collection; use Zalas\Toolbox\Tool\Command; use Zalas\Toolbox\Tool\Command\ComposerBinPluginCommand; +use Zalas\Toolbox\Tool\Command\ComposerBinPluginLinkCommand; use Zalas\Toolbox\Tool\Command\OptimisedComposerBinPluginCommand; class OptimisedComposerBinPluginCommandTest extends TestCase { public function test_it_is_a_command() { - $this->assertInstanceOf(Command::class, new OptimisedComposerBinPluginCommand(Collection::create([new ComposerBinPluginCommand('phpstan/phpstan', 'phpstan')]))); + $this->assertInstanceOf(Command::class, new OptimisedComposerBinPluginCommand(Collection::create([new ComposerBinPluginCommand('phpstan/phpstan', 'phpstan', Collection::create([]))]))); } public function test_it_groups_composer_bin_command_by_namespace() { $commands = [ - new ComposerBinPluginCommand('phpstan/phpstan', 'phpstan'), - new ComposerBinPluginCommand('phan/phan', 'tools'), - new ComposerBinPluginCommand('behat/behat', 'tools'), + new ComposerBinPluginCommand('phpstan/phpstan', 'phpstan', Collection::create([])), + new ComposerBinPluginCommand('phan/phan', 'tools', Collection::create([])), + new ComposerBinPluginCommand('behat/behat', 'tools', Collection::create([])), ]; $command = new OptimisedComposerBinPluginCommand(Collection::create($commands)); @@ -35,4 +36,55 @@ public function test_it_throws_an_exception_if_there_is_no_commands() new OptimisedComposerBinPluginCommand(Collection::create([])); } + + public function test_it_creates_links_to_composer_bin_commands() + { + $commands = [ + new ComposerBinPluginCommand( + 'phpstan/phpstan', + 'phpstan', + Collection::create([ + new ComposerBinPluginLinkCommand('phpstan', '/tools/phpstan', 'phpstan'), + new ComposerBinPluginLinkCommand('phpstan', '/other/path/phpstan', 'phpstan'), + ]) + ), + new ComposerBinPluginCommand( + 'phan/phan', + 'tools', + Collection::create([ + new ComposerBinPluginLinkCommand('phan', '/tools/phan', 'tools'), + ]) + ), + new ComposerBinPluginCommand( + 'behat/behat', + 'tools', + Collection::create([ + new ComposerBinPluginLinkCommand('behat', '/tools/behat', 'tools'), + ]) + ), + ]; + + $command = new OptimisedComposerBinPluginCommand(Collection::create($commands)); + + $this->assertRegExp('#composer global bin phpstan require .*? phpstan/phpstan && composer global bin tools require .*? phan/phan behat/behat#', (string) $command); + $this->assertRegExp('# && ln -sf.*?phpstan /tools/phpstan#', (string) $command); + $this->assertRegExp('# && ln -sf.*?phpstan /other/path/phpstan#', (string) $command); + $this->assertRegExp('# && ln -sf.*?phan /tools/phan#', (string) $command); + $this->assertRegExp('# && ln -sf.*?behat /tools/behat#', (string) $command); + $this->assertNotRegExp('#&&\s*&&#', (string) $command, 'It does not generate empty commands'); + } + + public function test_it_does_not_create_links_if_commands_have_no_links_defined() + { + $commands = [ + new ComposerBinPluginCommand('phpstan/phpstan', 'phpstan', Collection::create([])), + new ComposerBinPluginCommand('phan/phan', 'tools', Collection::create([])), + new ComposerBinPluginCommand('behat/behat', 'tools', Collection::create([])), + ]; + + $command = new OptimisedComposerBinPluginCommand(Collection::create($commands)); + + $this->assertNotRegExp('#ln -s#', (string) $command); + $this->assertNotRegExp('#&&\s*&&#', (string) $command, 'It does not generate empty commands'); + } } diff --git a/tests/UseCase/InstallToolsTest.php b/tests/UseCase/InstallToolsTest.php index 7e15f079..10228bcf 100644 --- a/tests/UseCase/InstallToolsTest.php +++ b/tests/UseCase/InstallToolsTest.php @@ -75,8 +75,8 @@ public function test_it_does_not_include_empty_commands() public function test_it_groups_composer_bin_plugin_commands() { $this->tools->all(Argument::type(Filter::class))->willReturn(Collection::create([ - $this->tool(new ComposerBinPluginCommand('phpstan/phpstan', 'tools')), - $this->tool(new ComposerBinPluginCommand('phan/phan', 'tools')), + $this->tool(new ComposerBinPluginCommand('phpstan/phpstan', 'tools', Collection::create([]))), + $this->tool(new ComposerBinPluginCommand('phan/phan', 'tools', Collection::create([]))), ])); $command = $this->useCase->__invoke($this->filter()); From dc353833e85ce7dee7f7ee2ca8d762ad004f0c28 Mon Sep 17 00:00:00 2001 From: Jakub Zalas Date: Sat, 23 Mar 2019 12:04:12 +0100 Subject: [PATCH 3/3] Downgrade box as it fails on PHP 7.3 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c30962aa..1e2c982f 100644 --- a/Makefile +++ b/Makefile @@ -149,4 +149,4 @@ tools/infection.pubkey: curl -Ls https://github.com/infection/infection/releases/download/0.10.6/infection.phar.pubkey -o tools/infection.pubkey tools/box: - curl -Ls https://github.com/humbug/box/releases/download/3.6.0/box.phar -o tools/box && chmod +x tools/box + curl -Ls https://github.com/humbug/box/releases/download/3.4.0/box.phar -o tools/box && chmod +x tools/box