From ea28665cafc777a3a13463ae8a1fa0e921e65f11 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 21 Nov 2025 00:22:56 +0000 Subject: [PATCH] add dynamic command --- src/LaravelSupportCommand.php | 149 +++++++++++++++++++ src/LaravelSupportCommandServiceProvider.php | 3 +- 2 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 src/LaravelSupportCommand.php diff --git a/src/LaravelSupportCommand.php b/src/LaravelSupportCommand.php new file mode 100644 index 0000000..9d39b79 --- /dev/null +++ b/src/LaravelSupportCommand.php @@ -0,0 +1,149 @@ +argument('command'); + $args = $this->option('args'); + $retryDelay = (int) $this->option('retry-delay'); + + // Build full command + $fullCommand = "php artisan {$commandToRun}"; + if (!empty($args)) { + $fullCommand .= ' ' . implode(' ', $args); + } + + $this->info("=== Laravel Support Command Monitor ==="); + $this->info("Monitoring: {$fullCommand}"); + $this->info("Retry delay: {$retryDelay}s"); + $this->line(''); + + // Kill existing processes on startup (Unix only) + if (!$this->isWindows()) { + $this->warn("Killing any existing '{$commandToRun}' processes..."); + exec("pkill -f 'artisan {$commandToRun}'"); + sleep(1); + } + + $restartCount = 0; + + while (true) { + if ($restartCount > 0) { + $this->warn("Restart #{$restartCount} - Cleaning up processes..."); + $this->killExistingProcesses($commandToRun); + sleep(1); + } + + $this->info("[" . date('Y-m-d H:i:s') . "] Starting: {$fullCommand}"); + + $process = proc_open( + $fullCommand, + [ + 0 => ['pipe', 'r'], // stdin + 1 => ['pipe', 'w'], // stdout + 2 => ['pipe', 'w'], // stderr + ], + $pipes, + base_path(), // Working directory + null + ); + + if (is_resource($process)) { + // Close stdin as we don't need it + fclose($pipes[0]); + + // Set streams to non-blocking + stream_set_blocking($pipes[1], false); + stream_set_blocking($pipes[2], false); + + $status = proc_get_status($process); + $this->comment("Process started with PID: {$status['pid']}"); + + // Monitor output + while (true) { + $status = proc_get_status($process); + + // Check if process is still running + if (!$status['running']) { + $exitCode = $status['exitcode']; + $this->newLine(); + $this->error("Process exited with code: {$exitCode}"); + break; + } + + // Read stdout + while ($line = fgets($pipes[1])) { + $this->line(trim($line)); + } + + // Read stderr + while ($err = fgets($pipes[2])) { + $this->error(trim($err)); + } + + usleep(100000); // Sleep 0.1s to prevent CPU spinning + } + + // Clean up + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + + $restartCount++; + $this->warn("Retrying in {$retryDelay} seconds..."); + sleep($retryDelay); + + } else { + $this->error('Failed to start process. Retrying in ' . $retryDelay . ' seconds...'); + sleep($retryDelay); + } + } + } + + /** + * Check if running on Windows + */ + protected function isWindows(): bool + { + return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'; + } + + /** + * Kill existing processes + */ + protected function killExistingProcesses(string $command): void + { + if ($this->isWindows()) { + // Windows: harder to target specific commands + exec('taskkill /F /FI "WINDOWTITLE eq ' . $command . '" 2>NUL'); + } else { + // Unix: kill by command pattern + exec("pkill -f 'artisan {$command}' 2>/dev/null"); + } + } +} \ No newline at end of file diff --git a/src/LaravelSupportCommandServiceProvider.php b/src/LaravelSupportCommandServiceProvider.php index cca28ff..626be17 100644 --- a/src/LaravelSupportCommandServiceProvider.php +++ b/src/LaravelSupportCommandServiceProvider.php @@ -26,7 +26,8 @@ class LaravelSupportCommandServiceProvider extends ServiceProvider if ($this->app->runningInConsole()) { $this->commands([ QueueWorkCommand::class, - ScheduleWorkCommand::class + ScheduleWorkCommand::class, + LaravelSupportCommand::class ]); } }