From 3243c34d3adaaf8c8a6ec39c4e4473bb98f62d0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20V=C3=A1radi?= Date: Thu, 17 Jul 2025 17:37:14 +0200 Subject: [PATCH 1/3] Allow defining successful exit codes --- .../Console/Scheduling/ManagesAttributes.php | 33 +++++++++++++++++++ .../Console/Scheduling/ScheduleRunCommand.php | 15 ++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Console/Scheduling/ManagesAttributes.php b/src/Illuminate/Console/Scheduling/ManagesAttributes.php index 1a18378eb62f..34e34b5babc6 100644 --- a/src/Illuminate/Console/Scheduling/ManagesAttributes.php +++ b/src/Illuminate/Console/Scheduling/ManagesAttributes.php @@ -2,6 +2,7 @@ namespace Illuminate\Console\Scheduling; +use Illuminate\Support\Arr; use Illuminate\Support\Reflector; trait ManagesAttributes @@ -97,6 +98,13 @@ trait ManagesAttributes */ public $description; + /** + * The allowed exit codes additional to 0. + * + * @var array|true + */ + public array|true $allowExitCodes = []; + /** * Set which user the command should run as. * @@ -110,6 +118,31 @@ public function user($user) return $this; } + /** + * Set which exit codes should be accepted additional to 0. + * + * @param int|array $exitCode + * @return $this + */ + public function allowExitCode(int|array $exitCode): static + { + $this->allowExitCodes = Arr::wrap($exitCode); + + return $this; + } + + /** + * Accept all exit codes as successful. + * + * @return $this + */ + public function allowFailure(): static + { + $this->allowExitCodes = true; + + return $this; + } + /** * Limit the environments the command should run in. * diff --git a/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php b/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php index 0f0ad16d2c46..dff6ba951d2a 100644 --- a/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php +++ b/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php @@ -203,7 +203,7 @@ protected function runEvent($event) $this->eventsRan = true; - if ($event->exitCode != 0 && ! $event->runInBackground) { + if ($this->shouldFailOnExitCode($event) && ! $event->runInBackground) { throw new Exception("Scheduled command [{$event->command}] failed with exit code [{$event->exitCode}]."); } } catch (Throwable $e) { @@ -286,4 +286,17 @@ protected function clearInterruptSignal() { $this->cache->forget('illuminate:schedule:interrupt'); } + + /** + * Determine if the scheduled command should be considered failed based on its exit code. + * + * @param \Illuminate\Console\Scheduling\Event $event + * @return bool + */ + protected function shouldFailOnExitCode(Event $event): bool + { + return $event->allowExitCodes !== true && + $event->exitCode != 0 && + ! in_array($event->exitCode, $event->allowExitCodes); + } } From 48b11774f20915a14ee4451eb7b799e3a7652c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20V=C3=A1radi?= Date: Thu, 17 Jul 2025 17:38:39 +0200 Subject: [PATCH 2/3] Add tests for allowed exit codes --- .../Scheduling/ScheduleRunCommandTest.php | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/tests/Integration/Console/Scheduling/ScheduleRunCommandTest.php b/tests/Integration/Console/Scheduling/ScheduleRunCommandTest.php index 1ede51aa3f24..a0028546eb28 100644 --- a/tests/Integration/Console/Scheduling/ScheduleRunCommandTest.php +++ b/tests/Integration/Console/Scheduling/ScheduleRunCommandTest.php @@ -58,6 +58,68 @@ public function test_failing_command_in_foreground_triggers_event() }); } + /** + * @throws BindingResolutionException + */ + public function test_failing_command_with_expected_exit_code_in_foreground_does_not_trigger_event() + { + Event::fake([ + ScheduledTaskStarting::class, + ScheduledTaskFinished::class, + ScheduledTaskFailed::class, + ]); + + // Create a schedule and add the command + $schedule = $this->app->make(Schedule::class); + $task = $schedule->exec('exit 1') + ->everyMinute() + ->allowExitCode(1); + + // Make sure it will run regardless of schedule + $task->when(function () { + return true; + }); + + // Execute the scheduler + $this->artisan('schedule:run'); + + // Verify the event sequence + Event::assertDispatched(ScheduledTaskStarting::class); + Event::assertDispatched(ScheduledTaskFinished::class); + Event::assertNotDispatched(ScheduledTaskFailed::class); + } + + /** + * @throws BindingResolutionException + */ + public function test_failing_command_with_allowed_failures_in_foreground_does_not_trigger_event() + { + Event::fake([ + ScheduledTaskStarting::class, + ScheduledTaskFinished::class, + ScheduledTaskFailed::class, + ]); + + // Create a schedule and add the command + $schedule = $this->app->make(Schedule::class); + $task = $schedule->exec('exit 1') + ->everyMinute() + ->allowFailure(); + + // Make sure it will run regardless of schedule + $task->when(function () { + return true; + }); + + // Execute the scheduler + $this->artisan('schedule:run'); + + // Verify the event sequence + Event::assertDispatched(ScheduledTaskStarting::class); + Event::assertDispatched(ScheduledTaskFinished::class); + Event::assertNotDispatched(ScheduledTaskFailed::class); + } + /** * @throws BindingResolutionException */ From 84766ed80066a0b078e76a5a88b119c5d0d7377f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20V=C3=A1radi?= Date: Thu, 17 Jul 2025 17:39:15 +0200 Subject: [PATCH 3/3] Add exception for unexpected exit codes --- .../Console/Scheduling/ScheduleRunCommand.php | 3 +-- .../Scheduling/UnexpectedExitCodeException.php | 13 +++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 src/Illuminate/Console/Scheduling/UnexpectedExitCodeException.php diff --git a/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php b/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php index dff6ba951d2a..df5915170c80 100644 --- a/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php +++ b/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php @@ -2,7 +2,6 @@ namespace Illuminate\Console\Scheduling; -use Exception; use Illuminate\Console\Application; use Illuminate\Console\Command; use Illuminate\Console\Events\ScheduledTaskFailed; @@ -204,7 +203,7 @@ protected function runEvent($event) $this->eventsRan = true; if ($this->shouldFailOnExitCode($event) && ! $event->runInBackground) { - throw new Exception("Scheduled command [{$event->command}] failed with exit code [{$event->exitCode}]."); + throw new UnexpectedExitCodeException($event->command, $event->exitCode); } } catch (Throwable $e) { $this->dispatcher->dispatch(new ScheduledTaskFailed($event, $e)); diff --git a/src/Illuminate/Console/Scheduling/UnexpectedExitCodeException.php b/src/Illuminate/Console/Scheduling/UnexpectedExitCodeException.php new file mode 100644 index 000000000000..6e9ca0867146 --- /dev/null +++ b/src/Illuminate/Console/Scheduling/UnexpectedExitCodeException.php @@ -0,0 +1,13 @@ +