add dynamic command

This commit is contained in:
root 2025-11-21 00:22:56 +00:00
parent bbf15e0e61
commit ea28665caf
2 changed files with 151 additions and 1 deletions

View File

@ -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");
}
}
}

View File

@ -26,7 +26,8 @@ class LaravelSupportCommandServiceProvider extends ServiceProvider
if ($this->app->runningInConsole()) {
$this->commands([
QueueWorkCommand::class,
ScheduleWorkCommand::class
ScheduleWorkCommand::class,
LaravelSupportCommand::class
]);
}
}