Skip to content

Commit

Permalink
Rewrite the crontab recipe to work with sections
Browse files Browse the repository at this point in the history
  • Loading branch information
ardentsword committed Oct 12, 2023
1 parent 6166a6c commit 6ee657f
Showing 1 changed file with 39 additions and 80 deletions.
119 changes: 39 additions & 80 deletions contrib/crontab.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
/*
Recipe for adding crontab jobs.
It checks for duplicates by the command part of the job. Changing the schedule will update the crontab. So when you change the command part you have to manually remove the old one. Use `crontab -e` on the server to remove it.
This recipe creates a new section in the crontab file with the configured jobs.
The section is identified by the *crontab:identifier* variable, by default the application name.
## Configuration
Expand All @@ -20,117 +21,75 @@
]);
```
*/

namespace Deployer;

// Get path to bin
set('bin/crontab', function () {
return which('crontab');
});

desc('Loads crontab');
task('crontab:load', function () {
set('crontab:all', []);

// Crontab is empty
if (!test ("{{bin/crontab}} -l >> /dev/null 2>&1")) {
return;
}

$cronData = run ("{{bin/crontab}} -l");
$cronLines = explode (PHP_EOL, $cronData);

$currentTasks = [];
foreach ($cronLines as $cronLine) {
$jobData = parseJob($cronLine);
if (is_null ($jobData)) {
continue;
}

$currentTasks[$jobData['ckey']] = $jobData;
}

set ('crontab:all', $currentTasks);
set('crontab:identifier', function () {
return get('application', 'application');
});

desc('Sync crontab jobs');
task('crontab:sync', function () {
$syncJobs = get('crontab:jobs', []);
$cronJobsLocal = get('crontab:jobs', []);

if (count ($syncJobs) == 0) {
if (count($cronJobsLocal) == 0) {
writeln("Nothing to sync - configure crontab:jobs");
return;
}

// Load current jobs
invoke('crontab:load');
$cronJobs = get('crontab:all');

foreach ($syncJobs as $syncJob) {
$syncJob = parse($syncJob);
$syncJobData = parseJob($syncJob);

if (is_null ($syncJobData)) {
continue;
}
$cronJobs = getRemoteCrontab();
$identifier = get('crontab:identifier');
$sectionStart = "###< $identifier";
$sectionEnd = "###> $identifier";

$cronJobData = $cronJobs[$syncJobData['ckey']] ?? NULL;
// find our cronjob section
$start = array_search($sectionStart, $cronJobs);
$end = array_search($sectionEnd, $cronJobs);

if (!is_null ($cronJobData) && $cronJobData['skey'] == $syncJobData['skey']) {
// Job is exists and correct
writeLn($syncJobData['cmd'] . ': <fg=green;options=bold>OK</>');
}
else {
if (is_null ($cronJobData)) {
writeLn($syncJobData['cmd'] . ': <fg=yellow;options=bold>NEW</>');
}
else {
writeLn($syncJobData['cmd'] . ': <fg=red;options=bold>FIX</>');
}

$cronJobs[$syncJobData['ckey']] = $syncJobData;
if ($start === false || $end === false) {
// Section is not found, create the section
$cronJobs[] = $sectionStart;
foreach ($cronJobsLocal as $cronJobLocal) {
$cronJobs[] = parse($cronJobLocal);
}
$cronJobs[] = $sectionEnd;
writeln("Crontab: Found no section, created the section with configured jobs");
} else {
// Replace the existing section
array_splice($cronJobs, $start + 1, $end - $start - 1, $cronJobsLocal);
writeln("Crontab: Found existing section, replaced with configured jobs");
}

$tmpCrontabPath = \sprintf('/tmp/%s', \uniqid('crontab_save_'));
setRemoteCrontab($cronJobs);
});

function setRemoteCrontab(array $lines): void
{
$tmpCrontabPath = sprintf('/tmp/%s', uniqid('crontab_save_'));

if (test("[ -f '$tmpCrontabPath' ]")) {
run("unlink '$tmpCrontabPath'");
}

foreach ($cronJobs as $cronJob) {
$jobString = $cronJob['minute'] . ' ' . $cronJob['hour'] . ' ' . $cronJob['day'] . ' ' . $cronJob['month'] . ' ' . $cronJob['weekday'] . ' ' . $cronJob['cmd'];
run("echo '" . $jobString . "' >> $tmpCrontabPath");
foreach ($lines as $line) {
run("echo '" . $line . "' >> $tmpCrontabPath");
}

run('{{bin/crontab}} ' . $tmpCrontabPath);
run('unlink ' . $tmpCrontabPath);
});


function parseJob ($job) {
if (!is_string($job)) {
return NULL;
}

if (substr ($job, 0, 1) == '#') {
return NULL;
}

$jobData = explode (' ', $job, 6);
}

if (count ($jobData) != 6) {
return NULL;
function getRemoteCrontab(): array
{
if (!test("{{bin/crontab}} -l >> /dev/null 2>&1")) {
return [];
}

return [
'skey' => md5 ($job),
'ckey' => md5 ($jobData['5']),
'minute' => $jobData['0'],
'hour' => $jobData['1'],
'day' => $jobData['2'],
'month' => $jobData['3'],
'weekday' => $jobData['4'],
'cmd' => $jobData['5'],
];
return explode(PHP_EOL, run("{{bin/crontab}} -l"));
}

0 comments on commit 6ee657f

Please sign in to comment.