Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API Update API to reflect changes in CLI interaction #93

Merged
merged 1 commit into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,14 @@ class TestCron implements CronTask
}
```

Run `vendor/bin/sake dev/build flush=1` to make Silverstripe aware of the new
Run `vendor/bin/sake db:build --flush` to make Silverstripe aware of the new
module.

Then execute the crontask controller, it's preferable you do this via the CLI
since that is how the server will execute it.

```
vendor/bin/sake dev/cron
vendor/bin/sake cron-task
```

Server configuration
Expand All @@ -96,7 +96,7 @@ most common way is by adding a file to the `/etc/cron.d/` directory.
First find the correct command to execute, for example:

```
/usr/bin/php /path/to/silverstripe/docroot/vendor/bin/sake dev/cron
/usr/bin/php /path/to/silverstripe/docroot/vendor/bin/sake cron-task
```

Then find out which user the webserver is running on, for example `www-data`.
Expand All @@ -110,7 +110,7 @@ sudo vim /etc/cron.d/silverstripe-crontask
The content of that file should be:

```
* * * * * www-data /usr/bin/php /path/to/silverstripe/docroot/vendor/bin/sake dev/cron
* * * * * www-data /usr/bin/php /path/to/silverstripe/docroot/vendor/bin/sake cron-task
```

This will run every minute as the www-data user and check if there are any
Expand All @@ -122,7 +122,7 @@ adding quiet=1 - for example

```
MAILTO=admin@example.com
* * * * * www-data /usr/bin/php /path/to/silverstripe/docroot/framework/cli-script.php dev/cron quiet=1
* * * * * www-data /usr/bin/php /path/to/silverstripe/docroot/vendor/bin/sake cron-task --quiet
```

**Warning**: Observe that the crontask module doesn't do any checking. If
Expand Down
13 changes: 3 additions & 10 deletions _config/crontask.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
---
Name: crontask
---
SilverStripe\Dev\DevelopmentAdmin:
registered_controllers:
cron:
controller: SilverStripe\CronTask\Controllers\CronTaskController
links:
cron: 'Run registered SilverStripe cron tasks'

SilverStripe\Control\Director:
rules:
'dev/cron/$Action': SilverStripe\CronTask\Controllers\CronTaskController
SilverStripe\Cli\Sake:
commands:
cron-task: 'SilverStripe\CronTask\Cli\CronTaskCommand'
117 changes: 117 additions & 0 deletions src/Cli/CronTaskCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?php

namespace SilverStripe\CronTask\Cli;

use Cron\CronExpression;
use DateTime;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\CronTask\CronTaskStatus;
use SilverStripe\CronTask\Interfaces\CronTask;
use SilverStripe\ORM\FieldType\DBDatetime;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
* This command finds, checks and processes all crontasks
* Hidden because there's no reason to run this manually other than for debugging
*/
#[AsCommand('cron-task', 'Runs cron tasks that are scheduled to be run', aliases: ['dev/cron'], hidden: true)]
class CronTaskCommand extends Command
{
/**
* Determine if a task should be run
*/
public function isTaskDue(CronTask $task, CronExpression $cron): bool
{
// Get last run status
$status = CronTaskStatus::get_status(get_class($task));

// If the cron is due immediately, then run it
$now = new DateTime(DBDatetime::now()->getValue());
if ($cron->isDue($now)) {
if (empty($status) || empty($status->LastRun)) {
return true;
}
// In case this process is invoked twice in one minute, supress subsequent executions
$lastRun = new DateTime($status->LastRun);
return $lastRun->format('Y-m-d H:i') != $now->format('Y-m-d H:i');
}

// If this is the first time this task is ever checked, no way to detect postponed execution
if (empty($status) || empty($status->LastChecked)) {
return false;
}

// Determine if we have passed the last expected run time
$nextExpectedDate = $cron->getNextRunDate($status->LastChecked);
return $nextExpectedDate <= $now;
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
// Check each task
$tasks = ClassInfo::implementorsOf(CronTask::class);
if (empty($tasks)) {
$this->output(
_t(
CronTaskCommand::class . '.NO_IMPLEMENTERS',
'There are no implementators of CronTask to run'
),
$output
);
return Command::SUCCESS;
}
foreach ($tasks as $subclass) {
$task = Injector::inst()->create($subclass);
// falsey schedule = don't run task
if ($task->getSchedule()) {
$this->runTask($task, $output);
}
}
return Command::SUCCESS;
}

/**
* Checks and runs a single CronTask
*/
public function runTask(CronTask $task, OutputInterface $output): void
{
$cron = CronExpression::factory($task->getSchedule());
$isDue = $this->isTaskDue($task, $cron);
// Update status of this task prior to execution in case of interruption
CronTaskStatus::update_status(get_class($task), $isDue);
if ($isDue) {
$this->output(
_t(
CronTaskCommand::class . '.WILL_START_NOW',
'{task} will start now.',
['task' => get_class($task)]
),
$output
);
$task->process();
} else {
$this->output(
_t(
CronTaskCommand::class . '.WILL_RUN_AT',
'{task} will run at {time}.',
['task' => get_class($task), 'time' => $cron->getNextRunDate()->format('Y-m-d H:i:s')]
),
$output,
OutputInterface::VERBOSITY_VERBOSE
);
}
}

/**
* Output a message including the timestamp
*/
public function output(string $message, OutputInterface $output, int $verbosity = 0): void
{
$timestamp = DBDatetime::now()->Rfc2822();
$output->writeln($timestamp . ' - ' . $message, $verbosity);
}
}
192 changes: 0 additions & 192 deletions src/Controllers/CronTaskController.php

This file was deleted.

4 changes: 2 additions & 2 deletions src/Interfaces/CronTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
namespace SilverStripe\CronTask\Interfaces;

/**
* By implementing this interface a /dev/cron will be able to start in on the
* expression that you return frmo getSchedule();
* By implementing this interface `sake cron-task` will be able to start in on the
* expression that you return from getSchedule();
*
* @package crontask
*/
Expand Down
Loading
Loading