add dynamic command
This commit is contained in:
parent
bbf15e0e61
commit
ea28665caf
|
|
@ -0,0 +1,149 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LaravelSupportCommand;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class LaravelSupportCommand extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'LaravelSupportCommand
|
||||||
|
{command : The artisan command to monitor (e.g., schedule:work, queue:work)}
|
||||||
|
{--args=* : Additional arguments for the command}
|
||||||
|
{--retry-delay=5 : Seconds to wait before retry on failure}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Monitor and auto-restart any Laravel artisan command if it exits unexpectedly';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$commandToRun = $this->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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -26,7 +26,8 @@ class LaravelSupportCommandServiceProvider extends ServiceProvider
|
||||||
if ($this->app->runningInConsole()) {
|
if ($this->app->runningInConsole()) {
|
||||||
$this->commands([
|
$this->commands([
|
||||||
QueueWorkCommand::class,
|
QueueWorkCommand::class,
|
||||||
ScheduleWorkCommand::class
|
ScheduleWorkCommand::class,
|
||||||
|
LaravelSupportCommand::class
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue