Browse Source

Fix tests and make TTY usage on ProcessExecutor cleaner

Jordi Boggiano 5 years ago
parent
commit
006c3de542

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

@@ -174,7 +174,7 @@ class EventDispatcher
                 $flags = $event->getFlags();
                 if (substr($callable, 0, 10) === '@composer ') {
                     $exec = $this->getPhpExecCommand() . ' ' . ProcessExecutor::escape(getenv('COMPOSER_BINARY')) . ' ' . implode(' ', $args);
-                    if (0 !== ($exitCode = $this->process->execute($exec, $ignoredOutput, null, $this->io->isInteractive()))) {
+                    if (0 !== ($exitCode = $this->executeTty($exec))) {
                         $this->io->writeError(sprintf('<error>Script %s handling the %s event returned with error code '.$exitCode.'</error>', $callable, $event->getName()), true, IOInterface::QUIET);
 
                         throw new ScriptExecutionException('Error Output: '.$this->process->getErrorOutput(), $exitCode);
@@ -248,7 +248,7 @@ class EventDispatcher
                     }
                 }
 
-                if (0 !== ($exitCode = $this->process->execute($exec, $ignoredOutput, null, $this->io->isInteractive()))) {
+                if (0 !== ($exitCode = $this->executeTty($exec))) {
                     $this->io->writeError(sprintf('<error>Script %s handling the %s event returned with error code '.$exitCode.'</error>', $callable, $event->getName()), true, IOInterface::QUIET);
 
                     throw new ScriptExecutionException('Error Output: '.$this->process->getErrorOutput(), $exitCode);
@@ -265,6 +265,15 @@ class EventDispatcher
         return $return;
     }
 
+    protected function executeTty($exec)
+    {
+        if ($this->io->isInteractive()) {
+            return $this->process->executeTty($exec);
+        }
+
+        return $this->process->execute($exec);
+    }
+
     protected function getPhpExecCommand()
     {
         $finder = new PhpExecutableFinder();

+ 29 - 3
src/Composer/Util/ProcessExecutor.php

@@ -15,6 +15,7 @@ namespace Composer\Util;
 use Composer\IO\IOInterface;
 use Symfony\Component\Process\Process;
 use Symfony\Component\Process\ProcessUtils;
+use Symfony\Component\Process\Exception\RuntimeException;
 
 /**
  * @author Robert Schönthal <seroscho@googlemail.com>
@@ -41,7 +42,28 @@ class ProcessExecutor
      * @param  string $cwd     the working directory
      * @return int    statuscode
      */
-    public function execute($command, &$output = null, $cwd = null, $tty = false)
+    public function execute($command, &$output = null, $cwd = null)
+    {
+        if (func_num_args() > 1) {
+            return $this->doExecute($command, $cwd, false, $output);
+        }
+
+        return $this->doExecute($command, $cwd, false);
+    }
+
+    /**
+     * runs a process on the commandline in TTY mode
+     *
+     * @param  string $command the command to execute
+     * @param  string $cwd     the working directory
+     * @return int    statuscode
+     */
+    public function executeTty($command, $cwd = null)
+    {
+        return $this->doExecute($command, $cwd, true);
+    }
+
+    private function doExecute($command, $cwd, $tty, &$output = null)
     {
         if ($this->io && $this->io->isDebug()) {
             $safeCommand = preg_replace_callback('{://(?P<user>[^:/\s]+):(?P<password>[^@\s/]+)@}i', function ($m) {
@@ -61,7 +83,7 @@ class ProcessExecutor
             $cwd = realpath(getcwd());
         }
 
-        $this->captureOutput = func_num_args() > 1;
+        $this->captureOutput = func_num_args() > 3;
         $this->errorOutput = null;
 
         // TODO in v3, commands should be passed in as arrays of cmd + args
@@ -71,7 +93,11 @@ class ProcessExecutor
             $process = new Process($command, $cwd, null, null, static::getTimeout());
         }
         if (!Platform::isWindows() && $tty) {
-            $process->setTty(true);
+            try {
+                $process->setTty(true);
+            } catch (RuntimeException $e) {
+                // ignore TTY enabling errors
+            }
         }
 
         $callback = is_callable($output) ? $output : array($this, 'outputHandler');

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

@@ -538,6 +538,10 @@ class EventDispatcherTest extends TestCase
             ->willReturn('> exit 1');
 
         $io->expects($this->at(2))
+            ->method('isInteractive')
+            ->willReturn(1);
+
+        $io->expects($this->at(3))
             ->method('writeError')
             ->with($this->equalTo('<error>Script '.$code.' handling the post-install-cmd event returned with error code 1</error>'));