1
0
Fork 0

Fix tests and make TTY usage on ProcessExecutor cleaner

pull/8566/head
Jordi Boggiano 2020-01-31 16:33:34 +01:00
parent f572636628
commit 006c3de542
No known key found for this signature in database
GPG Key ID: 7BBD42C429EC80BC
3 changed files with 44 additions and 5 deletions

View File

@ -174,7 +174,7 @@ class EventDispatcher
$flags = $event->getFlags(); $flags = $event->getFlags();
if (substr($callable, 0, 10) === '@composer ') { if (substr($callable, 0, 10) === '@composer ') {
$exec = $this->getPhpExecCommand() . ' ' . ProcessExecutor::escape(getenv('COMPOSER_BINARY')) . ' ' . implode(' ', $args); $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); $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); 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); $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); throw new ScriptExecutionException('Error Output: '.$this->process->getErrorOutput(), $exitCode);
@ -265,6 +265,15 @@ class EventDispatcher
return $return; return $return;
} }
protected function executeTty($exec)
{
if ($this->io->isInteractive()) {
return $this->process->executeTty($exec);
}
return $this->process->execute($exec);
}
protected function getPhpExecCommand() protected function getPhpExecCommand()
{ {
$finder = new PhpExecutableFinder(); $finder = new PhpExecutableFinder();

View File

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

View File

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