SchedulerCommand.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. <?php
  2. /**
  3. * @package Grav\Console\Cli
  4. *
  5. * @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
  6. * @license MIT License; see LICENSE file for details.
  7. */
  8. namespace Grav\Console\Cli;
  9. use Cron\CronExpression;
  10. use Grav\Common\Grav;
  11. use Grav\Common\Utils;
  12. use Grav\Common\Scheduler\Scheduler;
  13. use Grav\Console\GravCommand;
  14. use RocketTheme\Toolbox\Event\Event;
  15. use Symfony\Component\Console\Helper\Table;
  16. use Symfony\Component\Console\Input\InputOption;
  17. use function is_null;
  18. /**
  19. * Class SchedulerCommand
  20. * @package Grav\Console\Cli
  21. */
  22. class SchedulerCommand extends GravCommand
  23. {
  24. /**
  25. * @return void
  26. */
  27. protected function configure(): void
  28. {
  29. $this
  30. ->setName('scheduler')
  31. ->addOption(
  32. 'install',
  33. 'i',
  34. InputOption::VALUE_NONE,
  35. 'Show Install Command'
  36. )
  37. ->addOption(
  38. 'jobs',
  39. 'j',
  40. InputOption::VALUE_NONE,
  41. 'Show Jobs Summary'
  42. )
  43. ->addOption(
  44. 'details',
  45. 'd',
  46. InputOption::VALUE_NONE,
  47. 'Show Job Details'
  48. )
  49. ->addOption(
  50. 'run',
  51. 'r',
  52. InputOption::VALUE_OPTIONAL,
  53. 'Force run all jobs or a specific job if you specify a specific Job ID',
  54. false
  55. )
  56. ->setDescription('Run the Grav Scheduler. Best when integrated with system cron')
  57. ->setHelp("Running without any options will force the Scheduler to run through it's jobs and process them");
  58. }
  59. /**
  60. * @return int
  61. */
  62. protected function serve(): int
  63. {
  64. $this->initializePlugins();
  65. $grav = Grav::instance();
  66. $grav['backups']->init();
  67. $this->initializePages();
  68. $this->initializeThemes();
  69. /** @var Scheduler $scheduler */
  70. $scheduler = $grav['scheduler'];
  71. $grav->fireEvent('onSchedulerInitialized', new Event(['scheduler' => $scheduler]));
  72. $input = $this->getInput();
  73. $io = $this->getIO();
  74. $error = 0;
  75. $run = $input->getOption('run');
  76. if ($input->getOption('jobs')) {
  77. // Show jobs list
  78. $jobs = $scheduler->getAllJobs();
  79. $job_states = (array)$scheduler->getJobStates()->content();
  80. $rows = [];
  81. $table = new Table($io);
  82. $table->setStyle('box');
  83. $headers = ['Job ID', 'Command', 'Run At', 'Status', 'Last Run', 'State'];
  84. $io->title('Scheduler Jobs Listing');
  85. foreach ($jobs as $job) {
  86. $job_status = ucfirst($job_states[$job->getId()]['state'] ?? 'ready');
  87. $last_run = $job_states[$job->getId()]['last-run'] ?? 0;
  88. $status = $job_status === 'Failure' ? "<red>{$job_status}</red>" : "<green>{$job_status}</green>";
  89. $state = $job->getEnabled() ? '<cyan>Enabled</cyan>' : '<red>Disabled</red>';
  90. $row = [
  91. $job->getId(),
  92. "<white>{$job->getCommand()}</white>",
  93. "<magenta>{$job->getAt()}</magenta>",
  94. $status,
  95. '<yellow>' . ($last_run === 0 ? 'Never' : date('Y-m-d H:i', $last_run)) . '</yellow>',
  96. $state,
  97. ];
  98. $rows[] = $row;
  99. }
  100. if (!empty($rows)) {
  101. $table->setHeaders($headers);
  102. $table->setRows($rows);
  103. $table->render();
  104. } else {
  105. $io->text('no jobs found...');
  106. }
  107. $io->newLine();
  108. $io->note('For error details run "bin/grav scheduler -d"');
  109. $io->newLine();
  110. } elseif ($input->getOption('details')) {
  111. $jobs = $scheduler->getAllJobs();
  112. $job_states = (array)$scheduler->getJobStates()->content();
  113. $io->title('Job Details');
  114. $table = new Table($io);
  115. $table->setStyle('box');
  116. $table->setHeaders(['Job ID', 'Last Run', 'Next Run', 'Errors']);
  117. $rows = [];
  118. foreach ($jobs as $job) {
  119. $job_state = $job_states[$job->getId()];
  120. $error = isset($job_state['error']) ? trim($job_state['error']) : false;
  121. /** @var CronExpression $expression */
  122. $expression = $job->getCronExpression();
  123. $next_run = $expression->getNextRunDate();
  124. $row = [];
  125. $row[] = $job->getId();
  126. if (!is_null($job_state['last-run'])) {
  127. $row[] = '<yellow>' . date('Y-m-d H:i', $job_state['last-run']) . '</yellow>';
  128. } else {
  129. $row[] = '<yellow>Never</yellow>';
  130. }
  131. $row[] = '<yellow>' . $next_run->format('Y-m-d H:i') . '</yellow>';
  132. if ($error) {
  133. $row[] = "<error>{$error}</error>";
  134. } else {
  135. $row[] = '<green>None</green>';
  136. }
  137. $rows[] = $row;
  138. }
  139. $table->setRows($rows);
  140. $table->render();
  141. } elseif ($run !== false && $run !== null) {
  142. $io->title('Force Run Job: ' . $run);
  143. $job = $scheduler->getJob($run);
  144. if ($job) {
  145. $job->inForeground()->run();
  146. if ($job->isSuccessful()) {
  147. $io->success('Job ran successfully...');
  148. } else {
  149. $error = 1;
  150. $io->error('Job failed to run successfully...');
  151. }
  152. $output = $job->getOutput();
  153. if ($output) {
  154. $io->write($output);
  155. }
  156. } else {
  157. $error = 1;
  158. $io->error('Could not find a job with id: ' . $run);
  159. }
  160. } elseif ($input->getOption('install')) {
  161. $io->title('Install Scheduler');
  162. $verb = 'install';
  163. if ($scheduler->isCrontabSetup()) {
  164. $io->success('All Ready! You have already set up Grav\'s Scheduler in your crontab. You can validate this by running "crontab -l" to list your current crontab entries.');
  165. $verb = 'reinstall';
  166. } else {
  167. $user = $scheduler->whoami();
  168. $error = 1;
  169. $io->error('Can\'t find a crontab for ' . $user . '. You need to set up Grav\'s Scheduler in your crontab');
  170. }
  171. if (!Utils::isWindows()) {
  172. $io->note("To $verb, run the following command from your terminal:");
  173. $io->newLine();
  174. $io->text(trim($scheduler->getCronCommand()));
  175. } else {
  176. $io->note("To $verb, create a scheduled task in Windows.");
  177. $io->text('Learn more at https://learn.getgrav.org/advanced/scheduler');
  178. }
  179. } else {
  180. // Run scheduler
  181. $force = $run === null;
  182. $scheduler->run(null, $force);
  183. if ($input->getOption('verbose')) {
  184. $io->title('Running Scheduled Jobs');
  185. $io->text($scheduler->getVerboseOutput());
  186. }
  187. }
  188. return $error;
  189. }
  190. }