Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions src/Cli/IO.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,17 @@ public static function writeln(string|array $messages): void
self::writeToLogsFile($messages);
}

/**
* Write on stdout as is: text that looks like Symfony Console tags (`<info>`, `<href=...>`) is not interpreted.
* For text that doesn't come from the CLI, e.g. the output of a command run on Lambda.
* @param string|string[] $messages
*/
public static function writeRaw(string|array $messages): void
{
self::safeWrite($messages, OutputInterface::OUTPUT_RAW);
self::writeToLogsFile($messages);
}

/**
* Log at the verbose level.
* @param string|string[] $messages
Expand Down Expand Up @@ -202,8 +213,9 @@ private static function doLogVerbose(string|array $messages): void

/**
* @param string|string[] $messages
* @param OutputInterface::OUTPUT_NORMAL|OutputInterface::OUTPUT_RAW $options
*/
private static function safeWrite(string|array $messages): void
private static function safeWrite(string|array $messages, int $options = OutputInterface::OUTPUT_NORMAL): void
{
if (OutputInterface::VERBOSITY_QUIET === self::$output->getVerbosity()) return;

Expand All @@ -228,7 +240,7 @@ private static function safeWrite(string|array $messages): void
stream_set_blocking(STDOUT, true);
stream_set_blocking(STDERR, true);

self::$output->writeln($messages);
self::$output->writeln($messages, $options);

// Render the spinner again
self::$spinner?->render();
Expand Down
10 changes: 6 additions & 4 deletions src/Commands/Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int

if ($invocation['status'] === 'success') {
IO::spinClear();
IO::writeln($invocation['output']);
// As is: the output of the command is not formatted for Symfony Console
IO::writeRaw($invocation['output']);
return 0;
}

Expand Down Expand Up @@ -86,7 +87,7 @@ private function writeErrorDetails(string $output): void
|| ! isset($errorDetails['errorType'], $errorDetails['errorMessage'])
|| ! is_string($errorDetails['errorType'])
|| ! is_string($errorDetails['errorMessage'])) {
IO::writeln(Styles::red($output));
IO::writeRaw(Styles::red($output));
return;
}

Expand All @@ -100,14 +101,15 @@ private function writeErrorDetails(string $output): void
'',
Styles::bold(Styles::red('ERROR')) . ' ' . Styles::gray($errorType),
'',
$errorDetails['errorMessage'],
]);
// The output of the command
IO::writeRaw($errorDetails['errorMessage']);
if (isset($errorDetails['stackTrace']) && is_array($errorDetails['stackTrace'])) {
$stackTrace = array_values(array_filter($errorDetails['stackTrace'], 'is_string'));
IO::verbose($stackTrace);
}
} catch (JsonException) {
IO::writeln(Styles::red($output));
IO::writeRaw(Styles::red($output));
}
}
}
23 changes: 21 additions & 2 deletions tests/Commands/CommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,34 @@ public function test_no_colors_when_the_output_is_not_a_terminal(): void
$this->assertFalse($this->requestBodies['/api/v1/commands/start']['ansi']);
}

public function test_the_output_is_printed_as_is(): void
{
// Text that looks like Symfony Console tags is not interpreted: it's the output of the application
$output = 'Hello <info>world</info>, <href=https://example.com>a link</>';

[$status, $display] = $this->runCommand(decorated: true, command: ['status' => 'success', 'output' => $output]);
$this->assertSame(0, $status, $display);
$this->assertStringContainsString($output, $display);

[$status, $display] = $this->runCommand(decorated: false, command: [
'status' => 'failed',
'output' => json_encode(['errorType' => 'Bref\ConsoleRuntime\CommandFailed', 'errorMessage' => $output]),
]);
$this->assertSame(1, $status, $display);
$this->assertStringContainsString($output, $display);
}

/**
* @param array{status: string, output: string} $command What Bref Cloud returns for the command
* @return array{int, string} The exit code and the output
*/
private function runCommand(bool $decorated): array
private function runCommand(bool $decorated, array $command = ['status' => 'success', 'output' => "\e[32mLaravel\e[39m 13"]): array
{
$this->requestBodies = [];
$command = new Command($this->brefCloud([
'/api/v1/environments/find' => $this->environment(),
'/api/v1/commands/start' => ['id' => 5],
'/api/v1/commands/5' => ['status' => 'success', 'output' => "\e[32mLaravel\e[39m 13"],
'/api/v1/commands/5' => $command,
]));
$input = new ArrayInput(['args' => 'about', '--config' => $this->configFile]);
$input->setInteractive(false);
Expand Down
Loading