Browse Source

Call a script recursively with extra parameters (#7720)

* Added support for calling scripts recursively (fixes #7562)
Jérôme Deuchnord 6 years ago
parent
commit
b0b00ad1fa

+ 11 - 0
doc/articles/scripts.md

@@ -238,6 +238,17 @@ one by prefixing the command name with `@`:
 }
 ```
 
+You can also refer a script and pass it new arguments:
+
+```json
+{
+  "scripts": {
+    "tests": "phpunit",
+    "testsVerbose": "@tests -vvv"
+  }
+}
+```
+
 ## Calling Composer commands
 
 To call Composer commands, you can use `@composer` which will automatically

+ 6 - 2
src/Composer/EventDispatcher/EventDispatcher.php

@@ -176,8 +176,12 @@ class EventDispatcher
                 $return = false === call_user_func($callable, $event) ? 1 : 0;
             } elseif ($this->isComposerScript($callable)) {
                 $this->io->writeError(sprintf('> %s: %s', $event->getName(), $callable), true, IOInterface::VERBOSE);
-                $scriptName = substr($callable, 1);
-                $args = $event->getArguments();
+
+                $script = explode(' ', substr($callable, 1));
+                $scriptName = $script[0];
+                unset($script[0]);
+
+                $args = array_merge($script, $event->getArguments());
                 $flags = $event->getFlags();
                 if (substr($callable, 0, 10) === '@composer ') {
                     $exec = $this->getPhpExecCommand() . ' ' . ProcessExecutor::escape(getenv('COMPOSER_BINARY')) . substr($callable, 9);

+ 40 - 0
tests/Composer/Test/EventDispatcher/EventDispatcherTest.php

@@ -14,6 +14,7 @@ namespace Composer\Test\EventDispatcher;
 
 use Composer\EventDispatcher\Event;
 use Composer\EventDispatcher\EventDispatcher;
+use Composer\EventDispatcher\ScriptExecutionException;
 use Composer\Installer\InstallerEvents;
 use Composer\Config;
 use Composer\Composer;
@@ -274,6 +275,45 @@ class EventDispatcherTest extends TestCase
         $this->assertEquals($expected, $io->getOutput());
     }
 
+    public function testRecursionInScriptsNames()
+    {
+        $process = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
+        $dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
+            ->setConstructorArgs(array(
+                $composer = $this->createComposerInstance(),
+                $io = new BufferIO('', OutputInterface::VERBOSITY_VERBOSE),
+                $process
+            ))
+            ->setMethods(array(
+                'getListeners'
+            ))
+            ->getMock();
+
+        $process->expects($this->exactly(1))
+            ->method('execute')
+            ->will($this->returnValue(0));
+
+        $dispatcher->expects($this->atLeastOnce())
+            ->method('getListeners')
+            ->will($this->returnCallback(function (Event $event) {
+                if($event->getName() === 'hello') {
+                    return array('echo Hello');
+                }
+
+                if($event->getName() === 'helloWorld') {
+                    return array('@hello World');
+                }
+
+                return array();
+            }));
+
+        $dispatcher->dispatch('helloWorld', new CommandEvent('helloWorld', $composer, $io));
+        $expected = "> helloWorld: @hello World".PHP_EOL.
+            "> hello: echo Hello " .escapeshellarg('World').PHP_EOL;
+
+        $this->assertEquals($expected, $io->getOutput());
+    }
+
     /**
      * @expectedException RuntimeException
      */