Skip to content

Commit

Permalink
Handle SIGINT gracefully
Browse files Browse the repository at this point in the history
  • Loading branch information
jigarius committed Jan 3, 2024
1 parent 072d349 commit 7fbf7f7
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 4 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ command just like version 1.x.
- `drall exec:drush ...` is now `drall exec drush ...`
- `drall exec:shell ...` is now `drall exec ...`

#### Interrupting a command

When `drall exec` receives a signal to interrupt (usually `ctrl + c`), Drall
stops after processing the site that is currently being processed. This
prevents the current command from terminating abruptly. However, if a second
interrupt signal is received, then Drall stops immediately.

#### Drush with @@dir

In this method, the `--uri` option is sent to `drush`.
Expand Down
46 changes: 43 additions & 3 deletions src/Command/ExecCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Drall\Model\EnvironmentId;
use Drall\Model\Placeholder;
use Drall\Model\RawCommand;
use Drall\Trait\SignalAwareTrait;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
Expand All @@ -24,6 +25,8 @@
*/
class ExecCommand extends BaseCommand {

use SignalAwareTrait;

/**
* Maximum number of Drall workers.
*
Expand Down Expand Up @@ -163,11 +166,40 @@ protected function execute(InputInterface $input, OutputInterface $output): int
);
$exitCode = 0;

Loop::run(function () use ($values, $command, $placeholder, $output, $progressBar, $workers, &$exitCode) {
// Handle interruption signals to stop Drall gracefully.
$isStopping = FALSE;
$this->registerInterruptionListener(function () use (&$isStopping, $output) {
$output->writeln('');

// If a previous SIGINT was received, then stop immediately.
if ($isStopping) {
$this->logger->error('Interrupted by user.');
exit(1);
}

// Prepare to stop after the current item is processed.
$this->logger->warning('Stopping after current item.');
$isStopping = TRUE;
});

Loop::run(function () use (
$values,
$command,
$placeholder,
$output,
$progressBar,
$workers,
&$exitCode,
&$isStopping
) {
yield ConcurrentIterator\each(
Iterator\fromIterable($values),
new LocalSemaphore($workers),
function ($value) use ($command, $placeholder, $output, $progressBar, &$exitCode) {
function ($value) use ($command, $placeholder, $output, $progressBar, &$exitCode, &$isStopping) {
if ($isStopping) {
return;
}

$sCommand = Placeholder::replace([$placeholder->value => $value], $command);
$process = new Process("($sCommand) 2>&1", getcwd());

Expand All @@ -189,9 +221,17 @@ function ($value) use ($command, $placeholder, $output, $progressBar, &$exitCode
);
});

$progressBar->finish();
if (!$isStopping) {
$progressBar->finish();
}

$output->writeln('');

if ($isStopping) {
$this->logger->error('Interrupted by user.');
return 1;
}

return $exitCode;
}

Expand Down
17 changes: 17 additions & 0 deletions src/Trait/SignalAwareTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,21 @@ protected function registerSignalListener(int $signo, callable $handler): bool {
return TRUE;
}

/**
* Register a listener for SIGINT.
*
* @param callable $handler
* A callable event listener.
*
* @return bool
* True if the listener was registered.
*/
protected function registerInterruptionListener(callable $handler): bool {
if (!defined('SIGINT')) {
return FALSE;
}

return self::registerSignalListener(SIGINT, $handler);
}

}
2 changes: 1 addition & 1 deletion src/Trait/SiteDetectorAwareTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public function setSiteDetector(SiteDetector $siteDetector) {
public function siteDetector(): SiteDetector {
if (!$this->hasSiteDetector()) {
throw new \BadMethodCallException(
'A site detecetor instance must first be assigned'
'A site detector instance must first be assigned'
);
}

Expand Down

0 comments on commit 7fbf7f7

Please sign in to comment.