diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7f0c767a..75c28a9b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,21 +23,9 @@ jobs: strategy: fail-fast: true matrix: - php: [7.3, 7.4, '8.0', 8.1, 8.2, 8.3] - laravel: [8, 9, 10, 11] + php: ['8.0', 8.1, 8.2, 8.3] + laravel: [9, 10, 11] exclude: - - php: 7.3 - laravel: 9 - - php: 7.3 - laravel: 10 - - php: 7.3 - laravel: 11 - - php: 7.4 - laravel: 9 - - php: 7.4 - laravel: 10 - - php: 7.4 - laravel: 11 - php: '8.0' laravel: 10 - php: '8.0' diff --git a/composer.json b/composer.json index 91275675..8ce0634b 100644 --- a/composer.json +++ b/composer.json @@ -10,21 +10,21 @@ } ], "require": { - "php": "^7.3|^8.0", + "php": "^8.0", "ext-json": "*", "ext-pcntl": "*", "ext-posix": "*", - "illuminate/contracts": "^8.17|^9.0|^10.0|^11.0", - "illuminate/queue": "^8.17|^9.0|^10.0|^11.0", - "illuminate/support": "^8.17|^9.0|^10.0|^11.0", + "illuminate/contracts": "^9.21|^10.0|^11.0", + "illuminate/queue": "^9.21|^10.0|^11.0", + "illuminate/support": "^9.21|^10.0|^11.0", "nesbot/carbon": "^2.17|^3.0", "ramsey/uuid": "^4.0", - "symfony/process": "^5.0|^6.0|^7.0", - "symfony/error-handler": "^5.0|^6.0|^7.0" + "symfony/process": "^6.0|^7.0", + "symfony/error-handler": "^6.0|^7.0" }, "require-dev": { "mockery/mockery": "^1.0", - "orchestra/testbench": "^6.0|^7.0|^8.0|^9.0", + "orchestra/testbench": "^7.0|^8.0|^9.0", "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.0|^10.4", "predis/predis": "^1.1|^2.0" diff --git a/src/Console/ClearCommand.php b/src/Console/ClearCommand.php index d3a7e9f5..a9ba8aa5 100644 --- a/src/Console/ClearCommand.php +++ b/src/Console/ClearCommand.php @@ -41,7 +41,7 @@ public function handle(JobRepository $jobRepository, QueueManager $manager) } if (! method_exists(RedisQueue::class, 'clear')) { - $this->line('Clearing queues is not supported on this version of Laravel'); + $this->components->error('Clearing queues is not supported on this version of Laravel.'); return 1; } @@ -54,7 +54,7 @@ public function handle(JobRepository $jobRepository, QueueManager $manager) $count = $manager->connection($connection)->clear($queue); - $this->line('Cleared '.$count.' jobs from the ['.$queue.'] queue '); + $this->components->info('Cleared '.$count.' jobs from the ['.$queue.'] queue.'); return 0; } diff --git a/src/Console/ClearMetricsCommand.php b/src/Console/ClearMetricsCommand.php index 799c4c3b..35b503ac 100644 --- a/src/Console/ClearMetricsCommand.php +++ b/src/Console/ClearMetricsCommand.php @@ -31,6 +31,6 @@ public function handle(MetricsRepository $metrics) { $metrics->clear(); - $this->info('Metrics cleared successfully.'); + $this->components->info('Metrics cleared successfully.'); } } diff --git a/src/Console/ContinueCommand.php b/src/Console/ContinueCommand.php index 0cccc6b1..7a6050a3 100644 --- a/src/Console/ContinueCommand.php +++ b/src/Console/ContinueCommand.php @@ -36,12 +36,19 @@ public function handle(MasterSupervisorRepository $masters) return Str::startsWith($master->name, MasterSupervisor::basename()); })->all(); - foreach (Arr::pluck($masters, 'pid') as $processId) { - $this->info("Sending CONT Signal To Process: {$processId}"); + collect(Arr::pluck($masters, 'pid')) + ->whenNotEmpty(fn () => $this->components->info('Sending CONT signal to processes.')) + ->whenEmpty(fn () => $this->components->info('No processes to continue.')) + ->each(function ($processId) { + $result = true; - if (! posix_kill($processId, SIGCONT)) { - $this->error("Failed to kill process: {$processId} (".posix_strerror(posix_get_last_error()).')'); - } - } + $this->components->task("Process: $processId", function () use ($processId, &$result) { + return $result = posix_kill($processId, SIGCONT); + }); + + if (! $result) { + $this->components->error("Failed to kill process: {$processId} (".posix_strerror(posix_get_last_error()).')'); + } + })->whenNotEmpty(fn () => $this->output->writeln('')); } } diff --git a/src/Console/ContinueSupervisorCommand.php b/src/Console/ContinueSupervisorCommand.php index 793bd6ad..c5643b3a 100644 --- a/src/Console/ContinueSupervisorCommand.php +++ b/src/Console/ContinueSupervisorCommand.php @@ -38,15 +38,15 @@ public function handle(SupervisorRepository $supervisors) }))->pid; if (is_null($processId)) { - $this->error('Failed to find a supervisor with this name'); + $this->components->error('Failed to find a supervisor with this name'); return 1; } - $this->info("Sending CONT Signal To Process: {$processId}"); + $this->components->info("Sending CONT signal to process: {$processId}"); if (! posix_kill($processId, SIGCONT)) { - $this->error("Failed to send CONT signal to process: {$processId} (".posix_strerror(posix_get_last_error()).')'); + $this->components->error("Failed to send CONT signal to process: {$processId} (".posix_strerror(posix_get_last_error()).')'); } } } diff --git a/src/Console/ForgetFailedCommand.php b/src/Console/ForgetFailedCommand.php index 9ab3da15..20114f33 100644 --- a/src/Console/ForgetFailedCommand.php +++ b/src/Console/ForgetFailedCommand.php @@ -31,9 +31,9 @@ public function handle(JobRepository $repository) $repository->deleteFailed($this->argument('id')); if ($this->laravel['queue.failer']->forget($this->argument('id'))) { - $this->info('Failed job deleted successfully!'); + $this->components->info('Failed job deleted successfully!'); } else { - $this->error('No failed job matches the given ID.'); + $this->components->error('No failed job matches the given ID.'); return 1; } diff --git a/src/Console/HorizonCommand.php b/src/Console/HorizonCommand.php index 581bdb77..36fcf1f9 100644 --- a/src/Console/HorizonCommand.php +++ b/src/Console/HorizonCommand.php @@ -32,7 +32,7 @@ class HorizonCommand extends Command public function handle(MasterSupervisorRepository $masters) { if ($masters->find(MasterSupervisor::name())) { - return $this->comment('A master supervisor is already running on this machine.'); + return $this->components->warn('A master supervisor is already running on this machine.'); } $environment = $this->option('environment') ?? config('horizon.env') ?? config('app.env'); @@ -43,12 +43,14 @@ public function handle(MasterSupervisorRepository $masters) ProvisioningPlan::get(MasterSupervisor::name())->deploy($environment); - $this->info('Horizon started successfully.'); + $this->components->info('Horizon started successfully.'); pcntl_async_signals(true); pcntl_signal(SIGINT, function () use ($master) { - $this->line('Shutting down...'); + $this->output->writeln(''); + + $this->components->info('Shutting down.'); return $master->terminate(); }); diff --git a/src/Console/InstallCommand.php b/src/Console/InstallCommand.php index 422ba1fe..67f0926c 100644 --- a/src/Console/InstallCommand.php +++ b/src/Console/InstallCommand.php @@ -29,18 +29,17 @@ class InstallCommand extends Command */ public function handle() { - $this->comment('Publishing Horizon Service Provider...'); - $this->callSilent('vendor:publish', ['--tag' => 'horizon-provider']); + $this->components->info('Installing Horizon resources.'); - $this->comment('Publishing Horizon Assets...'); - $this->callSilent('vendor:publish', ['--tag' => 'horizon-assets']); - - $this->comment('Publishing Horizon Configuration...'); - $this->callSilent('vendor:publish', ['--tag' => 'horizon-config']); + collect([ + 'Assets' => fn () => $this->callSilent('vendor:publish', ['--tag' => 'horizon-assets']) == 0, + 'Service Provider' => fn () => $this->callSilent('vendor:publish', ['--tag' => 'horizon-provider']) == 0, + 'Configuration' => fn () => $this->callSilent('vendor:publish', ['--tag' => 'horizon-config']) == 0, + ])->each(fn ($task, $description) => $this->components->task($description, $task)); $this->registerHorizonServiceProvider(); - $this->info('Horizon scaffolding installed successfully.'); + $this->components->info('Horizon scaffolding installed successfully.'); } /** diff --git a/src/Console/ListCommand.php b/src/Console/ListCommand.php index 80f7ccd2..41a0f2b3 100644 --- a/src/Console/ListCommand.php +++ b/src/Console/ListCommand.php @@ -32,9 +32,11 @@ public function handle(MasterSupervisorRepository $masters) $masters = $masters->all(); if (empty($masters)) { - return $this->info('No machines are running.'); + return $this->components->info('No machines are running.'); } + $this->output->writeln(''); + $this->table([ 'Name', 'PID', 'Supervisors', 'Status', ], collect($masters)->map(function ($master) { @@ -47,5 +49,7 @@ public function handle(MasterSupervisorRepository $masters) $master->status, ]; })->all()); + + $this->output->writeln(''); } } diff --git a/src/Console/PauseCommand.php b/src/Console/PauseCommand.php index 1260d5ad..7b253afc 100644 --- a/src/Console/PauseCommand.php +++ b/src/Console/PauseCommand.php @@ -36,12 +36,19 @@ public function handle(MasterSupervisorRepository $masters) return Str::startsWith($master->name, MasterSupervisor::basename()); })->all(); - foreach (Arr::pluck($masters, 'pid') as $processId) { - $this->info("Sending USR2 Signal To Process: {$processId}"); + collect(Arr::pluck($masters, 'pid')) + ->whenNotEmpty(fn () => $this->components->info('Sending USR2 signal to processes.')) + ->whenEmpty(fn () => $this->components->info('No processes to pause.')) + ->each(function ($processId) { + $result = true; - if (! posix_kill($processId, SIGUSR2)) { - $this->error("Failed to kill process: {$processId} (".posix_strerror(posix_get_last_error()).')'); - } - } + $this->components->task("Process: $processId", function () use ($processId, &$result) { + return $result = posix_kill($processId, SIGUSR2); + }); + + if (! $result) { + $this->components->error("Failed to kill process: {$processId} (".posix_strerror(posix_get_last_error()).')'); + } + })->whenNotEmpty(fn () => $this->output->writeln('')); } } diff --git a/src/Console/PauseSupervisorCommand.php b/src/Console/PauseSupervisorCommand.php index c03e0c2c..ef9811a8 100644 --- a/src/Console/PauseSupervisorCommand.php +++ b/src/Console/PauseSupervisorCommand.php @@ -38,15 +38,15 @@ public function handle(SupervisorRepository $supervisors) }))->pid; if (is_null($processId)) { - $this->error('Failed to find a supervisor with this name'); + $this->components->error('Failed to find a supervisor with this name'); return 1; } - $this->info("Sending USR2 Signal To Process: {$processId}"); + $this->components->info("Sending USR2 signal to process: {$processId}"); if (! posix_kill($processId, SIGUSR2)) { - $this->error("Failed to send USR2 signal to process: {$processId} (".posix_strerror(posix_get_last_error()).')'); + $this->components->error("Failed to send USR2 signal to process: {$processId} (".posix_strerror(posix_get_last_error()).')'); } } } diff --git a/src/Console/PurgeCommand.php b/src/Console/PurgeCommand.php index d97ee144..16070d5c 100644 --- a/src/Console/PurgeCommand.php +++ b/src/Console/PurgeCommand.php @@ -96,13 +96,15 @@ public function purge($master, $signal = SIGTERM) $master, $this->supervisors->longestActiveTimeout() ); - collect($expired)->each(function ($processId) use ($master, $signal) { - $this->comment("Killing Process: {$processId}"); - - exec("kill -s {$signal} {$processId}"); - - $this->processes->forgetOrphans($master, [$processId]); - }); + collect($expired) + ->whenNotEmpty(fn () => $this->components->info('Sending TERM signal to expired processes of ['.$master.']')) + ->each(function ($processId) use ($master, $signal) { + $this->components->task("Process: $processId", function () use ($processId, $signal) { + exec("kill -s {$signal} {$processId}"); + }); + + $this->processes->forgetOrphans($master, [$processId]); + })->whenNotEmpty(fn () => $this->output->writeln('')); } /** @@ -118,12 +120,18 @@ protected function recordOrphans($master, $signal) $master, $orphans = $this->inspector->orphaned() ); - foreach ($orphans as $processId) { - $this->info("Observed Orphan: {$processId}"); + collect($orphans) + ->whenNotEmpty(fn () => $this->components->info('Sending TERM signal to orphaned processes of ['.$master.']')) + ->each(function ($processId) use ($signal) { + $result = true; - if (! posix_kill($processId, $signal)) { - $this->error("Failed to kill process for Orphan: {$processId} (".posix_strerror(posix_get_last_error()).')'); - } - } + $this->components->task("Process: $processId", function () use ($processId, $signal, &$result) { + return $result = posix_kill($processId, $signal); + }); + + if (! $result) { + $this->components->error("Failed to kill orphan process: {$processId} (".posix_strerror(posix_get_last_error()).')'); + } + })->whenNotEmpty(fn () => $this->output->writeln('')); } } diff --git a/src/Console/SnapshotCommand.php b/src/Console/SnapshotCommand.php index 5db99336..3718b371 100644 --- a/src/Console/SnapshotCommand.php +++ b/src/Console/SnapshotCommand.php @@ -34,7 +34,7 @@ public function handle(Lock $lock, MetricsRepository $metrics) if ($lock->get('metrics:snapshot', config('horizon.metrics.snapshot_lock', 300) - 30)) { $metrics->snapshot(); - $this->info('Metrics snapshot stored successfully.'); + $this->components->info('Metrics snapshot stored successfully.'); } } } diff --git a/src/Console/StatusCommand.php b/src/Console/StatusCommand.php index 5698c49c..ba47910a 100644 --- a/src/Console/StatusCommand.php +++ b/src/Console/StatusCommand.php @@ -30,7 +30,7 @@ class StatusCommand extends Command public function handle(MasterSupervisorRepository $masterSupervisorRepository) { if (! $masters = $masterSupervisorRepository->all()) { - $this->error('Horizon is inactive.'); + $this->components->error('Horizon is inactive.'); return 1; } @@ -38,12 +38,12 @@ public function handle(MasterSupervisorRepository $masterSupervisorRepository) if (collect($masters)->contains(function ($master) { return $master->status === 'paused'; })) { - $this->warn('Horizon is paused.'); + $this->components->warn('Horizon is paused.'); return 1; } - $this->info('Horizon is running.'); + $this->components->info('Horizon is running.'); return 0; } diff --git a/src/Console/SupervisorCommand.php b/src/Console/SupervisorCommand.php index aeb4773e..a4856c6c 100644 --- a/src/Console/SupervisorCommand.php +++ b/src/Console/SupervisorCommand.php @@ -68,7 +68,7 @@ public function handle(SupervisorFactory $factory) try { $supervisor->ensureNoDuplicateSupervisors(); } catch (Exception $e) { - $this->error('A supervisor with this name is already running.'); + $this->components->error('A supervisor with this name is already running.'); return 13; } diff --git a/src/Console/SupervisorsCommand.php b/src/Console/SupervisorsCommand.php index 693dde07..4272646f 100644 --- a/src/Console/SupervisorsCommand.php +++ b/src/Console/SupervisorsCommand.php @@ -32,9 +32,11 @@ public function handle(SupervisorRepository $supervisors) $supervisors = $supervisors->all(); if (empty($supervisors)) { - return $this->info('No supervisors are running.'); + return $this->components->info('No supervisors are running.'); } + $this->output->writeln(''); + $this->table([ 'Name', 'PID', 'Status', 'Workers', 'Balancing', ], collect($supervisors)->map(function ($supervisor) { @@ -48,5 +50,7 @@ public function handle(SupervisorRepository $supervisors) $supervisor->options['balance'], ]; })->all()); + + $this->output->writeln(''); } } diff --git a/src/Console/TerminateCommand.php b/src/Console/TerminateCommand.php index a45cf8a1..c0d8d476 100644 --- a/src/Console/TerminateCommand.php +++ b/src/Console/TerminateCommand.php @@ -48,13 +48,20 @@ public function handle(CacheFactory $cache, MasterSupervisorRepository $masters) return Str::startsWith($master->name, MasterSupervisor::basename()); })->all(); - foreach (Arr::pluck($masters, 'pid') as $processId) { - $this->info("Sending TERM Signal To Process: {$processId}"); + collect(Arr::pluck($masters, 'pid')) + ->whenNotEmpty(fn () => $this->components->info('Sending TERM signal to processes.')) + ->whenEmpty(fn () => $this->components->info('No processes to terminate.')) + ->each(function ($processId) { + $result = true; - if (! posix_kill($processId, SIGTERM)) { - $this->error("Failed to kill process: {$processId} (".posix_strerror(posix_get_last_error()).')'); - } - } + $this->components->task("Process: $processId", function () use ($processId, &$result) { + return $result = posix_kill($processId, SIGTERM); + }); + + if (! $result) { + $this->components->error("Failed to kill process: {$processId} (".posix_strerror(posix_get_last_error()).')'); + } + })->whenNotEmpty(fn () => $this->output->writeln('')); $this->laravel['cache']->forever('illuminate:queue:restart', $this->currentTime()); } diff --git a/src/Console/TimeoutCommand.php b/src/Console/TimeoutCommand.php index 2dd74fb2..f9173d47 100644 --- a/src/Console/TimeoutCommand.php +++ b/src/Console/TimeoutCommand.php @@ -38,6 +38,10 @@ public function handle() { $plan = ProvisioningPlan::get(MasterSupervisor::name())->plan; - $this->line(collect($plan[$this->argument('environment')] ?? [])->max('timeout') ?? 60); + $environment = $this->argument('environment'); + + $timeout = collect($plan[$this->argument('environment')] ?? [])->max('timeout') ?? 60; + + $this->components->info('Maximum timeout for '.$environment.' environment: '.$timeout.' seconds.'); } } diff --git a/src/ProcessPool.php b/src/ProcessPool.php index a9c43c48..2f07cec6 100644 --- a/src/ProcessPool.php +++ b/src/ProcessPool.php @@ -270,7 +270,7 @@ protected function stopTerminatingProcessesThatAreHanging() foreach ($this->terminatingProcesses as $process) { $timeout = $this->options->timeout; - if ($process['terminatedAt']->addSeconds($timeout)->lte(CarbonImmutable::now())) { + if ($process['terminatedAt']->addSeconds((int) $timeout)->lte(CarbonImmutable::now())) { $process['process']->stop(); } }