From 6ee657f9e24462ba4eb1789bace018d8999b6e09 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 12 Oct 2023 13:35:18 +0200 Subject: [PATCH] Rewrite the crontab recipe to work with sections --- contrib/crontab.php | 119 +++++++++++++++----------------------------- 1 file changed, 39 insertions(+), 80 deletions(-) diff --git a/contrib/crontab.php b/contrib/crontab.php index 72a8fb623..ff6288076 100644 --- a/contrib/crontab.php +++ b/contrib/crontab.php @@ -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 @@ -20,6 +21,7 @@ ]); ``` */ + namespace Deployer; // Get path to bin @@ -27,110 +29,67 @@ 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'] . ': OK'); - } - else { - if (is_null ($cronJobData)) { - writeLn($syncJobData['cmd'] . ': NEW'); - } - else { - writeLn($syncJobData['cmd'] . ': 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")); }