diff --git a/src/Tempest/Console/src/Actions/ExecuteConsoleCommand.php b/src/Tempest/Console/src/Actions/ExecuteConsoleCommand.php index a799c3105..c2c6466c4 100644 --- a/src/Tempest/Console/src/Actions/ExecuteConsoleCommand.php +++ b/src/Tempest/Console/src/Actions/ExecuteConsoleCommand.php @@ -21,7 +21,7 @@ public function __construct( ) { } - public function __invoke(string $commandName): ExitCode + public function __invoke(string $commandName): ExitCode|int { $callable = $this->getCallable($this->resolveCommandMiddleware($commandName)); diff --git a/src/Tempest/Console/src/Console.php b/src/Tempest/Console/src/Console.php index f5d6ffac1..11dc075d3 100644 --- a/src/Tempest/Console/src/Console.php +++ b/src/Tempest/Console/src/Console.php @@ -9,7 +9,7 @@ interface Console { - public function call(string $command): ExitCode; + public function call(string $command): ExitCode|int; public function readln(): string; diff --git a/src/Tempest/Console/src/ConsoleApplication.php b/src/Tempest/Console/src/ConsoleApplication.php index be1e155af..315c4ce4a 100644 --- a/src/Tempest/Console/src/ConsoleApplication.php +++ b/src/Tempest/Console/src/ConsoleApplication.php @@ -57,7 +57,13 @@ public function run(): void try { $exitCode = ($this->container->get(ExecuteConsoleCommand::class))($this->argumentBag->getCommandName()); - $this->container->get(Kernel::class)->shutdown($exitCode->value); + $exitCode = is_int($exitCode) ? $exitCode : $exitCode->value; + + if ($exitCode < 0 || $exitCode > 255) { + throw new InvalidExitCode($exitCode); + } + + $this->container->get(Kernel::class)->shutdown($exitCode); } catch (Throwable $throwable) { foreach ($this->appConfig->errorHandlers as $exceptionHandler) { $exceptionHandler->handleException($throwable); diff --git a/src/Tempest/Console/src/ConsoleMiddleware.php b/src/Tempest/Console/src/ConsoleMiddleware.php index ebd140461..f955431bd 100644 --- a/src/Tempest/Console/src/ConsoleMiddleware.php +++ b/src/Tempest/Console/src/ConsoleMiddleware.php @@ -8,5 +8,5 @@ interface ConsoleMiddleware { - public function __invoke(Invocation $invocation, ConsoleMiddlewareCallable $next): ExitCode; + public function __invoke(Invocation $invocation, ConsoleMiddlewareCallable $next): ExitCode|int; } diff --git a/src/Tempest/Console/src/ConsoleMiddlewareCallable.php b/src/Tempest/Console/src/ConsoleMiddlewareCallable.php index caeea0611..91edbafee 100644 --- a/src/Tempest/Console/src/ConsoleMiddlewareCallable.php +++ b/src/Tempest/Console/src/ConsoleMiddlewareCallable.php @@ -14,7 +14,7 @@ public function __construct( ) { } - public function __invoke(Invocation $invocation): ExitCode + public function __invoke(Invocation $invocation): ExitCode|int { return ($this->closure)($invocation); } diff --git a/src/Tempest/Console/src/Exceptions/ConsoleErrorHandler.php b/src/Tempest/Console/src/Exceptions/ConsoleErrorHandler.php index dab363c1b..d0e315685 100644 --- a/src/Tempest/Console/src/Exceptions/ConsoleErrorHandler.php +++ b/src/Tempest/Console/src/Exceptions/ConsoleErrorHandler.php @@ -5,9 +5,12 @@ namespace Tempest\Console\Exceptions; use Tempest\Console\Console; +use Tempest\Console\ExitCode; +use Tempest\Console\HasExitCode; use Tempest\Console\Input\ConsoleArgumentBag; use Tempest\Container\Tag; use Tempest\Core\ErrorHandler; +use Tempest\Core\Kernel; use Tempest\Highlight\Escape; use Tempest\Highlight\Highlighter; use Throwable; @@ -15,6 +18,7 @@ final readonly class ConsoleErrorHandler implements ErrorHandler { public function __construct( + private Kernel $kernel, #[Tag('console')] private Highlighter $highlighter, private Console $console, @@ -53,6 +57,10 @@ public function handleException(Throwable $throwable): void ->writeln('-v show more') ->writeln(); } + + $exitCode = $throwable instanceof HasExitCode ? $throwable->getExitCode() : ExitCode::ERROR; + + $this->kernel->shutdown($exitCode->value); } public function handleError(int $errNo, string $errstr, string $errFile, int $errLine): void diff --git a/src/Tempest/Console/src/ExitCode.php b/src/Tempest/Console/src/ExitCode.php index 4fe2807ec..a08a391f3 100644 --- a/src/Tempest/Console/src/ExitCode.php +++ b/src/Tempest/Console/src/ExitCode.php @@ -4,10 +4,20 @@ namespace Tempest\Console; +/** + * You're free to return any int between 0 and 255 from a console command as exit code. + * The meaning of these integer values will be determined by your application. + * Alternatively, Tempest provides a closed set of predefined exit codes via this enum. + * The exit codes listed here are used by Tempest and assigned a fixed meaning. + */ enum ExitCode: int { case SUCCESS = 0; case ERROR = 1; case INVALID = 2; case CANCELLED = 25; + case CANNOT_EXECUTE = 126; + case COMMAND_NOT_FOUND = 127; + case INVALID_EXIT_CODE = 128; + case TERMINATED = 130; } diff --git a/src/Tempest/Console/src/GenericConsole.php b/src/Tempest/Console/src/GenericConsole.php index f5f8e11de..504db2b07 100644 --- a/src/Tempest/Console/src/GenericConsole.php +++ b/src/Tempest/Console/src/GenericConsole.php @@ -43,7 +43,7 @@ public function __construct( ) { } - public function call(string $command): ExitCode + public function call(string $command): ExitCode|int { return ($this->executeConsoleCommand)($command); } diff --git a/src/Tempest/Console/src/HasExitCode.php b/src/Tempest/Console/src/HasExitCode.php new file mode 100644 index 000000000..642739739 --- /dev/null +++ b/src/Tempest/Console/src/HasExitCode.php @@ -0,0 +1,10 @@ +appConfig->environment; diff --git a/src/Tempest/Console/src/Middleware/ConsoleExceptionMiddleware.php b/src/Tempest/Console/src/Middleware/ConsoleExceptionMiddleware.php index 4fb7abf30..95aaf76a1 100644 --- a/src/Tempest/Console/src/Middleware/ConsoleExceptionMiddleware.php +++ b/src/Tempest/Console/src/Middleware/ConsoleExceptionMiddleware.php @@ -18,7 +18,7 @@ public function __construct( ) { } - public function __invoke(Invocation $invocation, ConsoleMiddlewareCallable $next): ExitCode + public function __invoke(Invocation $invocation, ConsoleMiddlewareCallable $next): ExitCode|int { try { return $next($invocation); diff --git a/src/Tempest/Console/src/Middleware/ForceMiddleware.php b/src/Tempest/Console/src/Middleware/ForceMiddleware.php index 6efedc126..449200feb 100644 --- a/src/Tempest/Console/src/Middleware/ForceMiddleware.php +++ b/src/Tempest/Console/src/Middleware/ForceMiddleware.php @@ -17,7 +17,7 @@ public function __construct(private Console $console) { } - public function __invoke(Invocation $invocation, ConsoleMiddlewareCallable $next): ExitCode + public function __invoke(Invocation $invocation, ConsoleMiddlewareCallable $next): ExitCode|int { if ($invocation->argumentBag->get('-f') || $invocation->argumentBag->get('force')) { if ($this->console instanceof GenericConsole) { diff --git a/src/Tempest/Console/src/Middleware/HelpMiddleware.php b/src/Tempest/Console/src/Middleware/HelpMiddleware.php index 0d53ca383..73a414a06 100644 --- a/src/Tempest/Console/src/Middleware/HelpMiddleware.php +++ b/src/Tempest/Console/src/Middleware/HelpMiddleware.php @@ -19,7 +19,7 @@ public function __construct( ) { } - public function __invoke(Invocation $invocation, ConsoleMiddlewareCallable $next): ExitCode + public function __invoke(Invocation $invocation, ConsoleMiddlewareCallable $next): ExitCode|int { if ($invocation->argumentBag->get('-h') || $invocation->argumentBag->get('help')) { $this->renderHelp($invocation->consoleCommand); diff --git a/src/Tempest/Console/src/Middleware/InvalidCommandMiddleware.php b/src/Tempest/Console/src/Middleware/InvalidCommandMiddleware.php index d70bb3276..806ed5c97 100644 --- a/src/Tempest/Console/src/Middleware/InvalidCommandMiddleware.php +++ b/src/Tempest/Console/src/Middleware/InvalidCommandMiddleware.php @@ -22,7 +22,7 @@ public function __construct( ) { } - public function __invoke(Invocation $invocation, ConsoleMiddlewareCallable $next): ExitCode + public function __invoke(Invocation $invocation, ConsoleMiddlewareCallable $next): ExitCode|int { try { return $next($invocation); @@ -31,7 +31,7 @@ public function __invoke(Invocation $invocation, ConsoleMiddlewareCallable $next } } - private function retry(Invocation $invocation, InvalidCommandException $exception): ExitCode + private function retry(Invocation $invocation, InvalidCommandException $exception): ExitCode|int { $this->console->writeln("Provide missing input:"); diff --git a/src/Tempest/Console/src/Middleware/OverviewMiddleware.php b/src/Tempest/Console/src/Middleware/OverviewMiddleware.php index 3381e4d81..89a7cdeed 100644 --- a/src/Tempest/Console/src/Middleware/OverviewMiddleware.php +++ b/src/Tempest/Console/src/Middleware/OverviewMiddleware.php @@ -22,7 +22,7 @@ public function __construct( ) { } - public function __invoke(Invocation $invocation, ConsoleMiddlewareCallable $next): ExitCode + public function __invoke(Invocation $invocation, ConsoleMiddlewareCallable $next): ExitCode|int { if (! $invocation->argumentBag->getCommandName()) { $this->renderOverview(showHidden: $invocation->argumentBag->has('--all', '-a')); diff --git a/src/Tempest/Console/src/Middleware/ResolveOrRescueMiddleware.php b/src/Tempest/Console/src/Middleware/ResolveOrRescueMiddleware.php index 0d363989f..01a9f7d24 100644 --- a/src/Tempest/Console/src/Middleware/ResolveOrRescueMiddleware.php +++ b/src/Tempest/Console/src/Middleware/ResolveOrRescueMiddleware.php @@ -21,7 +21,7 @@ public function __construct( ) { } - public function __invoke(Invocation $invocation, ConsoleMiddlewareCallable $next): ExitCode + public function __invoke(Invocation $invocation, ConsoleMiddlewareCallable $next): ExitCode|int { $consoleCommand = $this->consoleConfig->commands[$invocation->argumentBag->getCommandName()] ?? null; @@ -35,7 +35,7 @@ public function __invoke(Invocation $invocation, ConsoleMiddlewareCallable $next )); } - private function rescue(string $commandName): ExitCode + private function rescue(string $commandName): ExitCode|int { $this->console->writeln("Command {$commandName} not found"); @@ -86,7 +86,7 @@ private function getSimilarCommands(string $name): array return $similarCommands; } - private function runIntendedCommand(string $commandName): ExitCode + private function runIntendedCommand(string $commandName): ExitCode|int { return ($this->executeConsoleCommand)($commandName); } diff --git a/tests/Integration/Console/Fixtures/SpecificMiddleware.php b/tests/Integration/Console/Fixtures/SpecificMiddleware.php index 73441766b..22cf51c10 100644 --- a/tests/Integration/Console/Fixtures/SpecificMiddleware.php +++ b/tests/Integration/Console/Fixtures/SpecificMiddleware.php @@ -16,7 +16,7 @@ public function __construct(private Console $console) { } - public function __invoke(Invocation $invocation, ConsoleMiddlewareCallable $next): ExitCode + public function __invoke(Invocation $invocation, ConsoleMiddlewareCallable $next): ExitCode|int { $this->console->writeln('from middleware');