Skip to content
Open
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
46 changes: 46 additions & 0 deletions cli/Valet/CommandLine.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,52 @@ public function shellExec($command) {
return shell_exec($command);
}

/**
* Stream command output in real time and optionally collect matching lines.
*
* @param string $command
* @param callable|null $lineMatches Callback to check matching lines; must return `true`
* to collect the line for post-run analysis.
* @param callable|null $lineIsError Callback to check whether a line should be
* rendered as an error in real time. If omitted, the capture matcher is reused.
*
* @return array The collected output lines or an empty array if no lines were collected.
*/
public function streamCommandOutput($command, ?callable $lineMatches = null, ?callable $lineIsError = null): array {
$capturedLines = [];
$lineIsError = $lineIsError ?: $lineMatches;

// Open a process to execute the command and read its output.
$handle = popen("$command 2>&1", 'r');
while ($handle && !feof($handle)) {
$line = fgets($handle);
if ($line === false) {
break;
}

// Keep raw command output unless caller explicitly marks this line as an error.
if ($lineIsError && $lineIsError($line)) {
error($line, false, false, true);
}
else {
echo $line;
}

// If a callback is provided and the line matches the condition,
// then collect the line for post-run analysis.
if ($lineMatches && $lineMatches($line)) {
$capturedLines[] = trim($line);
}
}

// Close the process.
if ($handle) {
pclose($handle);
}

return $capturedLines;
}

/**
* Pass the given Valet command to the command line with elevated privileges using gsudo.
*
Expand Down
21 changes: 14 additions & 7 deletions cli/Valet/ShareTools/Ngrok.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,26 @@ public function start(string $site, int $port, array $options = []) {

$ngrok = realpath(valetBinPath() . 'ngrok.exe');

$ngrokCommand = "\"$ngrok\" http $site:$port " . $this->getConfig() . " $options";
// Log to stdout, log level info, and log format term for real-time output.
$logging = "--log=stdout --log-level=info --log-format=term";

$ngrokCommand = "\"$ngrok\" http $site:$port " . $this->getConfig() . " $options $logging";

info("Sharing $site...\n");
info("To output the public URL, please open a new terminal and run `valet fetch-share-url $site`");

$output = $this->cli->shellExec("$ngrokCommand 2>&1");
// Stream ngrok output in real time and collect error lines for post-run analysis.
// Shared matcher: use the same rule for live error styling and for post-run capture.
$isErrorLine = function ($line) {
return strpos($line, 'ERROR:') !== false;
};

if ($errors = strstr($output, "ERROR")) {
error($errors . PHP_EOL);
// Pass the same matcher once; CommandLine reuses it for error styling when no separate
// error callback is supplied.
$errorLines = $this->cli->streamCommandOutput($ngrokCommand, $isErrorLine);

if (strpos($errors, 'ERR_NGROK_121') !== false) {
info("To update ngrok yourself, please run `valet ngrok update` and then upgrade the config file by running `valet ngrok config upgrade`\n");
}
if (!empty($errorLines) && strpos(implode("\n", $errorLines), 'ERR_NGROK_121') !== false) {
info("\nTo update ngrok yourself, please run `valet ngrok update` and then upgrade the config file by running `valet ngrok config upgrade`\n");
}
}

Expand Down
19 changes: 15 additions & 4 deletions cli/includes/helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Symfony\Component\Console\Helper\TableSeparator;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Formatter\OutputFormatter;

if (!isset($_SERVER['HOME'])) {
$_SERVER['HOME'] = $_SERVER['USERPROFILE'];
Expand Down Expand Up @@ -62,11 +63,16 @@ function warning($output) {
*
* @param string $output
* @param bool $exception Optionally pass a boolean to indicate whether to throw an exception. If `true`, the error will be thrown as a `ValetException`. [default: `false`]
* @param bool $newline Whether to append a newline after the error output. [default: `true`]
* @param bool $escapeOutput Whether to escape the output to prevent formatting issues. [default: `false`]
*
* @throws RuntimeException
* @throws ValetException
*/
function error(string $output, $exception = false) {
function error(string $output, bool $exception = false, bool $newline = true, bool $escapeOutput = false) {

$errorOutput = (new ConsoleOutput())->getErrorOutput();

if (isset($_ENV['APP_ENV']) && $_ENV['APP_ENV'] === 'testing') {
throw new RuntimeException($output);
}
Expand All @@ -78,12 +84,17 @@ function error(string $output, $exception = false) {
usleep(1);

// Print the error message to the console.
(new ConsoleOutput())->getErrorOutput()->writeln("\n\n<error>$errors</error>");
$errorOutput->write("\n\n<error>$errors</error>", $newline);

exit();
}
else {
(new ConsoleOutput())->getErrorOutput()->writeln("<error>$output</error>");
// If escapeOutput is true, then escape the output to prevent any formatting issues.
if ($escapeOutput) {
$output = OutputFormatter::escape($output);
}

$errorOutput->write("<error>$output</error>", $newline);
}
}

Expand Down Expand Up @@ -385,4 +396,4 @@ function str_contains_any($haystack, $needles) {
}
}
return false;
}
}