Skip to content

Commit

Permalink
Capture signals and wait until child process exits to also exit, fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Seldaek committed Jul 20, 2022
1 parent cf97af0 commit 266c280
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 8 deletions.
11 changes: 9 additions & 2 deletions src/Composer/Console/Application.php
Expand Up @@ -17,6 +17,7 @@
use Composer\Util\Platform;
use Composer\Util\Silencer;
use LogicException;
use Seld\Signal\SignalHandler;
use Symfony\Component\Console\Application as BaseApplication;
use Symfony\Component\Console\Exception\CommandNotFoundException;
use Symfony\Component\Console\Helper\HelperSet;
Expand Down Expand Up @@ -93,7 +94,15 @@ public function __construct()
date_default_timezone_set(Silencer::call('date_default_timezone_get'));
}

$this->io = new NullIO();

if (!$shutdownRegistered) {
$signalHandler = SignalHandler::create([SignalHandler::SIGINT, SignalHandler::SIGTERM, SignalHandler::SIGHUP], function (string $signal, SignalHandler $handler) {
$this->io->writeError('Received '.$signal.', aborting', true, IOInterface::DEBUG);

$handler->exitWithLastSignal();
});

$shutdownRegistered = true;

register_shutdown_function(static function (): void {
Expand All @@ -107,8 +116,6 @@ public function __construct()
});
}

$this->io = new NullIO();

$this->initialWorkingDirectory = getcwd();

parent::__construct('Composer', Composer::getVersion());
Expand Down
2 changes: 2 additions & 0 deletions src/Composer/EventDispatcher/ScriptExecutionException.php
Expand Up @@ -13,6 +13,8 @@
namespace Composer\EventDispatcher;

/**
* Thrown when a script running an external process exits with a non-0 status code
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class ScriptExecutionException extends \RuntimeException
Expand Down
27 changes: 21 additions & 6 deletions src/Composer/Util/ProcessExecutor.php
Expand Up @@ -14,6 +14,8 @@

use Composer\IO\IOInterface;
use Composer\Pcre\Preg;
use Seld\Signal\SignalHandler;
use Symfony\Component\Process\Exception\ProcessSignaledException;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\RuntimeException;
use React\Promise\Promise;
Expand Down Expand Up @@ -125,15 +127,28 @@ private function doExecute($command, ?string $cwd, bool $tty, &$output = null):
$this->outputHandler($type, $buffer);
};

$process->run($callback);
try {
$signalHandler = SignalHandler::create([SignalHandler::SIGINT, SignalHandler::SIGTERM, SignalHandler::SIGHUP], function (string $signal) {
$this->io->writeError('Received '.$signal.', aborting when child process is done', true, IOInterface::DEBUG);
});

if ($this->captureOutput && !is_callable($output)) {
$output = $process->getOutput();
}
$process->run($callback);

if ($this->captureOutput && !is_callable($output)) {
$output = $process->getOutput();
}

$this->errorOutput = $process->getErrorOutput();
$this->errorOutput = $process->getErrorOutput();

return $process->getExitCode();
return $process->getExitCode();
} catch (ProcessSignaledException $e) {
if ($signalHandler->isTriggered()) {
// exiting as we were signaled and the child process exited too due to the signal
$signalHandler->exitWithLastSignal();
}
} finally {
$signalHandler->unregister();
}
}

/**
Expand Down

0 comments on commit 266c280

Please sign in to comment.