Skip to content

Commit

Permalink
Improve output formatting and verbosity; refs #91
Browse files Browse the repository at this point in the history
- Add support for --quiet
- Use ✔ and ✖ to indicate status
  • Loading branch information
jigarius committed Dec 27, 2024
1 parent 060e034 commit 75a3832
Show file tree
Hide file tree
Showing 8 changed files with 352 additions and 194 deletions.
4 changes: 2 additions & 2 deletions src/Command/BaseCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ protected function preExecute(InputInterface $input, OutputInterface $output) {
}

if ($group = $this->getDrallGroup($input)) {
$this->logger->debug('Detected group: {group}', ['group' => $group]);
$this->logger->info('Using group: {group}', ['group' => $group]);
}

if ($filter = $this->getDrallFilter($input)) {
$this->logger->debug('Detected filter: {filter}', ['filter' => $filter]);
$this->logger->info('Using filter: {filter}', ['filter' => $filter]);
}
}

Expand Down
88 changes: 51 additions & 37 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\Trait\SignalAwareTrait;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
Expand Down Expand Up @@ -91,15 +92,38 @@ protected function configure() {
}

protected function initialize(InputInterface $input, OutputInterface $output): void {
if (!method_exists($input, 'getRawTokens')) {
parent::initialize($input, $output);
return;
}

// Parts of the command after "exec".
$rawTokens = $input->getRawTokens(TRUE);

// If obsolete --drall-* options are present, then abort.
if (method_exists($input, 'getRawTokens')) {
foreach ($input->getRawTokens() as $token) {
if (str_starts_with($token, '--drall-')) {
$output->writeln(<<<EOT
foreach ($rawTokens as $token) {
if (str_starts_with($token, '--drall-')) {
$output->writeln(<<<EOT
In Drall 4.x, all --drall-* options have been renamed.
See https://github.com/jigarius/drall/issues/99
EOT);
throw new \RuntimeException('Obsolete options detected.');
throw new \RuntimeException('Obsolete options detected');
}
}

// If options are present, an options separator (--) is required.
if (!in_array('--', $rawTokens)) {
foreach ($rawTokens as $token) {
if (str_starts_with($token, '-')) {
$output->writeln(<<<EOT
When using options, a "--" must be placed before the command to be executed.
<comment>Incorrect:</comment> drall exec --dry-run drush --field=site core:status
<comment>Correct:</comment> drall exec --dry-run -- drush --field=site core:status
Notice the `--` between `--dry-run` and the word `drush`.
EOT);
throw new \RuntimeException('Missing options separator');
}
}
}
Expand Down Expand Up @@ -140,19 +164,19 @@ protected function execute(InputInterface $input, OutputInterface $output): int
// Display commands without executing them.
if ($input->getOption('dry-run')) {
foreach ($values as $value) {
$sCommand = Placeholder::replace([$placeholder->value => $value], $command);
$output->writeln("# Item: $value", OutputInterface::VERBOSITY_VERBOSE);
$output->writeln($sCommand);
$pCommand = Placeholder::replace([$placeholder->value => $value], $command);
$output->writeln("$value: Preview");
$output->writeln($pCommand, OutputInterface::VERBOSITY_QUIET);
}

return 0;
return Command::SUCCESS;
}

$progressBar = new ProgressBar(
$this->isProgressBarHidden($input) ? new NullOutput() : $output,
count($values)
);
$exitCode = 0;
$exitCode = Command::SUCCESS;

// Handle interruption signals to stop Drall gracefully.
$isStopping = FALSE;
Expand All @@ -162,7 +186,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
// If a previous SIGINT was received, then stop immediately.
if ($isStopping) {
$this->logger->error('Interrupted by user.');
exit(1);
exit(Command::FAILURE);
}

// Prepare to stop after the current item is processed.
Expand Down Expand Up @@ -194,14 +218,22 @@ function ($value) use ($command, $placeholder, $output, $progressBar, &$exitCode
yield $process->start();
$this->logger->debug('Running: {command}', ['command' => $sCommand]);

$sOutput = yield ByteStream\buffer($process->getStdout());
if (0 !== yield $process->join()) {
$exitCode = 1;
// @todo Improve formatting of headings.
$pOutput = yield ByteStream\buffer($process->getStdout());
$pStatus = 'Done';
$pIcon = '';
if (Command::SUCCESS !== yield $process->join()) {
$pStatus = 'Failed';
$pIcon = '';
$exitCode = Command::FAILURE;
}

$pMessage = "$pIcon $value: $pStatus";

$progressBar->clear();
$output->writeln("Finished: $value");
$output->write($sOutput);
// Always display command output, even in --quiet mode.
$output->writeln($pMessage, OutputInterface::VERBOSITY_QUIET);
$output->write($pOutput);

$progressBar->advance();
$progressBar->display();
Expand All @@ -217,7 +249,7 @@ function ($value) use ($command, $placeholder, $output, $progressBar, &$exitCode

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

return $exitCode;
Expand All @@ -241,29 +273,10 @@ function ($value) use ($command, $placeholder, $output, $progressBar, &$exitCode
* Output: drush st --fields=site
*/
private function getCommand(InputInterface $input, OutputInterface $output): ?string {
$rawTokens = $input->getRawTokens(TRUE);
if (!in_array('--', $rawTokens)) {
foreach ($rawTokens as $token) {
if (str_starts_with($token, '-')) {
$output->writeln(<<<EOT
When using options, a "--" must be placed before the command to be executed.
<comment>Incorrect:</comment> drall exec --dry-run drush --field=site core:status
<comment>Correct:</comment> drall exec --dry-run -- drush --field=site core:status
Notice the `--` between `--dry-run` and the word `drush`.
EOT);
$this->logger->error('Separator "--" must be used when using options.');
return NULL;
}
}
}

// @todo Throw an error if --drall-* options are present.
// Everything after the first "--" is treated as an argument. All such
// arguments are treated as parts of the command to be executed.
$command = implode(' ', $input->getArguments()['cmd']);
$this->logger->debug("Command: {command}", ['command' => $command]);
$this->logger->debug("Command received: {command}", ['command' => $command]);

if (
str_contains($command, 'drush') &&
Expand All @@ -272,6 +285,7 @@ private function getCommand(InputInterface $input, OutputInterface $output): ?st
// Inject --uri=@@dir for Drush commands without placeholders.
$command = preg_replace('/\b(drush) /', 'drush --uri=@@dir ', $command, -1);
$this->logger->debug('Injected --uri parameter for Drush command.');
$this->logger->notice("Command modified: {command}", ['command' => $command]);
}

return $command;
Expand Down
12 changes: 4 additions & 8 deletions src/Drall.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,12 @@ public function __construct() {
protected function configureIO(InputInterface $input, OutputInterface $output): void {
parent::configureIO($input, $output);

if ($input->hasParameterOption('--debug', TRUE)) {
if (
$input->hasParameterOption('--debug', TRUE) ||
$input->hasParameterOption('-d', TRUE)
) {
$output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
}
elseif ($input->hasParameterOption('--verbose', TRUE)) {
$output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
}
else {
$output->setVerbosity(OutputInterface::VERBOSITY_NORMAL);
}

// The parent::configureIO sets verbosity in a SHELL_VERBOSITY. This causes
// other Symfony Console apps to become verbose, for example, Drush. To
Expand All @@ -69,7 +66,6 @@ protected function getDefaultInputDefinition(): InputDefinition {
// Remove unneeded options.
$options = $definition->getOptions();
unset(
$options['quiet'],
$options['no-interaction'],
);
$definition->setOptions($options);
Expand Down
34 changes: 22 additions & 12 deletions src/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@ abstract class TestCase extends TestCaseBase {

const PATH_EMPTY_DRUPAL = '/opt/empty-drupal';

/**
* Normalizes console output by stripping unimportant spaces.
*
* @param string $output
* Raw output.
*
* @return string
* Normalized output.
*/
private static function normalizeString(string $output): string {
return preg_replace('@(\s+)\n@', "\n", $output);
}

/**
* Creates a temporary file path.
*
Expand Down Expand Up @@ -57,19 +70,16 @@ protected function createDrupalFinderStub(?string $root = NULL): DrupalFinderCom
return $drupalFinder;
}

/**
* Asserts Shell output ignoring unimportant whitespace.
*
* @param string $expected
* Expected output.
* @param mixed $actual
* Actual output.
* @param string $message
* Error message.
*/
protected function assertOutputEquals(string $expected, mixed $actual, string $message = ''): void {
$actual = preg_replace('@(\s+)\n@', "\n", $actual ?? '');
$this->assertEquals($expected, $actual, $message);
$this->assertEquals($expected, self::normalizeString($actual ?? ''), $message);
}

protected function assertOutputStartsWith(string $expected, mixed $actual, string $message = ''): void {
$this->assertStringStartsWith($expected, self::normalizeString($actual ?? ''), $message);
}

protected function assertOutputContainsString(string $expected, mixed $actual, string $message = ''): void {
$this->assertStringContainsString($expected, self::normalizeString($actual ?? ''), $message);
}

}
62 changes: 62 additions & 0 deletions test/Integration/Command/BaseCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

namespace Drall\Integration\Command;

use Drall\TestCase;
use Symfony\Component\Process\Process;

/**
* @testdox Base Command
* @covers \Drall\Command\BaseCommand
*/
class BaseCommandTest extends TestCase {

/**
* @testdox Detects --group.
*/
public function testWithGroup(): void {
$process1 = Process::fromShellCommandline(
'drall exec --group=bluish --dry-run -vv -- ./vendor/bin/drush st',
static::PATH_DRUPAL,
);
$process1->run();
$this->assertOutputStartsWith(<<<EOT
[info] Using group: bluish
EOT, $process1->getOutput());

// Short form.
$process1 = Process::fromShellCommandline(
'drall exec -g bluish --dry-run -vv -- ./vendor/bin/drush st',
static::PATH_DRUPAL,
);
$process1->run();
$this->assertOutputStartsWith(<<<EOT
[info] Using group: bluish
EOT, $process1->getOutput());
}

/**
* @testdox Detects --filter.
*/
public function testWithFilter(): void {
$process1 = Process::fromShellCommandline(
'drall exec --filter=leo --dry-run -vv -- ./vendor/bin/drush st',
static::PATH_DRUPAL,
);
$process1->run();
$this->assertOutputStartsWith(<<<EOT
[info] Using filter: leo
EOT, $process1->getOutput());

// Short form.
$process2 = Process::fromShellCommandline(
'drall exec -f leo --dry-run -vv -- ./vendor/bin/drush st',
static::PATH_DRUPAL,
);
$process2->run();
$this->assertOutputStartsWith(<<<EOT
[info] Using filter: leo
EOT, $process2->getOutput());
}

}
Loading

0 comments on commit 75a3832

Please sign in to comment.