|
|
@@ -0,0 +1,170 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+namespace App\Commands;
|
|
|
+
|
|
|
+use App\Service\Cron\CronJobInterface;
|
|
|
+use App\Service\Cron\IO\CommandLineIO;
|
|
|
+use App\Service\ServiceIterator\CronjobIterator;
|
|
|
+use Psr\Log\LoggerInterface;
|
|
|
+use RuntimeException;
|
|
|
+use Symfony\Component\Console\Attribute\AsCommand;
|
|
|
+use Symfony\Component\Console\Command\Command;
|
|
|
+use Symfony\Component\Console\Command\LockableTrait;
|
|
|
+use Symfony\Component\Console\Helper\ProgressBar;
|
|
|
+use Symfony\Component\Console\Input\InputArgument;
|
|
|
+use Symfony\Component\Console\Input\InputInterface;
|
|
|
+use Symfony\Component\Console\Input\InputOption;
|
|
|
+use Symfony\Component\Console\Output\OutputInterface;
|
|
|
+use Symfony\Contracts\Service\Attribute\Required;
|
|
|
+
|
|
|
+#[AsCommand(
|
|
|
+ name: 'ot:cron',
|
|
|
+ description: 'Executes cron jobs'
|
|
|
+)]
|
|
|
+class CronCommand extends Command
|
|
|
+{
|
|
|
+ use LockableTrait;
|
|
|
+
|
|
|
+ private const ACTION_LIST = 'list';
|
|
|
+ private const ACTION_RUN = 'run';
|
|
|
+ private const ACTION_RUN_ALL = 'run-all';
|
|
|
+ private const ACTIONS = [self::ACTION_LIST, self::ACTION_RUN, self::ACTION_RUN_ALL];
|
|
|
+
|
|
|
+ private InputInterface $input;
|
|
|
+ private OutputInterface $output;
|
|
|
+ private bool $silent;
|
|
|
+ private ProgressBar $progressBar;
|
|
|
+ private LoggerInterface $logger;
|
|
|
+ private CronjobIterator $cronjobIterator;
|
|
|
+
|
|
|
+ #[Required]
|
|
|
+ public function setLoggerInterface(LoggerInterface $logger): void
|
|
|
+ {
|
|
|
+ $this->logger = $logger;
|
|
|
+ }
|
|
|
+
|
|
|
+ #[Required]
|
|
|
+ public function setCronjobIterator(CronjobIterator $cronjobIterator): void
|
|
|
+ {
|
|
|
+ $this->cronjobIterator = $cronjobIterator;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function configure(): void
|
|
|
+ {
|
|
|
+ $this->addArgument(
|
|
|
+ 'action',
|
|
|
+ InputArgument::REQUIRED,
|
|
|
+ 'Action to execute among : ' . implode(', ', self::ACTIONS)
|
|
|
+ );
|
|
|
+ $this->addArgument(
|
|
|
+ 'jobs',
|
|
|
+ InputArgument::OPTIONAL,
|
|
|
+ "Name(s) of the cron-job(s) to execute with '" . self::ACTION_RUN . "' (comma-separated)"
|
|
|
+ );
|
|
|
+ $this->addOption(
|
|
|
+ 'preview',
|
|
|
+ 'p',
|
|
|
+ InputOption::VALUE_NONE,
|
|
|
+ 'Only preview the operations instead of executing it'
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function updateProgression(int $i, int $total): void
|
|
|
+ {
|
|
|
+ if (!$this->progressBar->getMaxSteps() !== $total) {
|
|
|
+ $this->progressBar->setMaxSteps($total);
|
|
|
+ }
|
|
|
+ $this->progressBar->setProgress($i);
|
|
|
+ }
|
|
|
+
|
|
|
+ final protected function execute(InputInterface $input, OutputInterface $output): int
|
|
|
+ {
|
|
|
+ $this->input = $input;
|
|
|
+ $this->output = $output;
|
|
|
+
|
|
|
+ $action = $input->getArgument('action');
|
|
|
+ $jobNames = $input->getArgument('jobs');
|
|
|
+ $preview = $input->getOption('preview');
|
|
|
+
|
|
|
+ if (!in_array($action, self::ACTIONS, true)) {
|
|
|
+ $this->output->writeln('Error: unrecognized action');
|
|
|
+ return Command::INVALID;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($action === 'list') {
|
|
|
+ $this->listJobs();
|
|
|
+ return Command::SUCCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+ $jobs = [];
|
|
|
+
|
|
|
+ if ($action === self::ACTION_RUN_ALL) {
|
|
|
+ $jobs = $this->cronjobIterator->getAll();
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($action === self::ACTION_RUN) {
|
|
|
+ foreach (explode(',', $jobNames) as $name) {
|
|
|
+ try {
|
|
|
+ $jobs[] = $this->cronjobIterator->getByName($name);
|
|
|
+ } catch (\RuntimeException $e) {
|
|
|
+ $this->output->writeln($e->getMessage());
|
|
|
+ $this->listJobs();
|
|
|
+ return Command::INVALID;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach ($jobs as $job) {
|
|
|
+ $this->runJob($job, $preview);
|
|
|
+ }
|
|
|
+
|
|
|
+ return Command::SUCCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+ private function listJobs(): void {
|
|
|
+ $availableJobs = $this->cronjobIterator->getAll();
|
|
|
+ if (empty($availableJobs)) {
|
|
|
+ $this->output->writeln('No cronjob found');
|
|
|
+ } else {
|
|
|
+ $this->output->writeln('Available cron jobs : ');
|
|
|
+ foreach ($this->cronjobIterator->getAll() as $job) {
|
|
|
+ $this->output->writeln('* ' . $job->name());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private function runJob(CronjobInterface $job, bool $preview = false): int
|
|
|
+ {
|
|
|
+ if (!$this->lock($job->name())) {
|
|
|
+ $this->output->writeln('The command ' . $job->name() . ' is already running in another process. Abort.');
|
|
|
+ return Command::SUCCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+ $t0 = microtime(true);
|
|
|
+
|
|
|
+ $this->output->writeln("Started : " . $job->name());
|
|
|
+ if ($preview) {
|
|
|
+ $this->output->writeln('PREVIEW MODE');
|
|
|
+ }
|
|
|
+
|
|
|
+ $io = new CommandLineIO($job->name(), $this->logger, $this->output);
|
|
|
+ $job->setIO($io);
|
|
|
+
|
|
|
+ try {
|
|
|
+ if ($preview) {
|
|
|
+ $job->preview();
|
|
|
+ } else {
|
|
|
+ $job->execute();
|
|
|
+ }
|
|
|
+ } catch (RuntimeException $e) {
|
|
|
+ $this->logger->critical($e);
|
|
|
+ $this->output->write("An error happened while running the process : " . $e);
|
|
|
+ return Command::FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ $t1 = microtime(true);
|
|
|
+ $this->output->writeln($job->name() . " has been successfully executed [" . ($t1 - $t0) . " sec.]");
|
|
|
+
|
|
|
+ return Command::SUCCESS;
|
|
|
+ }
|
|
|
+}
|