Skip to content

Instantly share code, notes, and snippets.

@kalabro
Created November 21, 2016 07:25
Show Gist options
  • Save kalabro/470aaf49b9727c0c0a795ea7daf69eb6 to your computer and use it in GitHub Desktop.
Save kalabro/470aaf49b9727c0c0a795ea7daf69eb6 to your computer and use it in GitHub Desktop.
Duuu! Drupal Auto-Update script. Dirty and Old fashioned.
<?php
/**
* Duuu! Drupal Auto-Update script. Last tested with Drush 7.0.
* TODO:
* - convert to command.
*
* Requirements:
* - Drush 7 **OR** Core Update module enabled.
* - curl (optionally for HTTP site check).
*
* Usage:
* > drush scr /path/to/duuu.php --r /www/drupal -l https://example.com --security-only [email protected] -y
*
*/
// Projects to update. Leave empty to update all. Use --security-only flag to check security releases only.
$projects = array(); // All projects mode.
//$projects = array('drupal'); // Core only mode.
$email = drush_get_option('email', variable_get('site_mail'));
$bcc = '[email protected]';
$messages = array();
$backup_dir = drush_get_option('backup-dir', drush_server_home() . '/' . 'drush-backups');
$backup_dir = drush_trim_path($backup_dir) . '/duuu/' . gmdate('Y-m-d_His', $_SERVER['REQUEST_TIME']);
// Set one backup dir for all further backups.
drush_set_context('DRUSH_BACKUP_DIR', $backup_dir);
drush_prepare_backup_dir();
// 1. Check if there are security updates of Drupal core.
drush_log('1. Check if there are new updates.');
// Requires update module for Drush < 7.
drush_invoke_process('@self', 'cc', array('drush'));
$return = drush_invoke_process('@self', 'pm-updatestatus', $projects, array('security-only' => drush_get_option('security-only')), FALSE);
$candidates = array();
foreach ($return['object'] as $project => $project_data) {
if (empty($projects) || in_array($project, $projects)) {
if ($project_data['existing_version'] != $project_data['candidate_version']) {
$candidates[$project] = $project . '-' . $project_data['candidate_version'];
}
}
}
if (empty($candidates)) {
drush_log(dt('No code updates available.'), 'ok');
drush_delete_dir($backup_dir);
return FALSE;
}
drush_print_r($candidates);
// 2. Backup database.
drush_log('2. Backup database.');
$return = drush_invoke_process('@self', 'sql-dump', array(), array(
'result-file' => $backup_dir . '/database.sql',
'gzip' => TRUE
), FALSE);
if ($return['error_status'] || empty($return['object'])) {
return drush_set_error('DRUSH_SQL_DUMP_FAILED', dt('sql-dump failed.'));
}
$backup_sql = $return['object'];
drush_log($backup_sql);
// 3. Move site to maintenance mode.
drush_log('3. Move site to maintenance mode.');
drush_invoke_process('@self', 'vset', array(
'maintenance_mode',
'1'
), array('exact' => TRUE), FALSE);
// 4. Enable Update module.
drush_log('4. Enable Update module.');
$update_is_enabled_by_script = FALSE;
if (!module_exists('update')) {
$return = drush_invoke_process('@self', 'en', array('update'), array(), FALSE);
$update_is_enabled_by_script = TRUE;
}
// 5. Duuu it finally!
drush_log('5. Duuu it finally!');
$success = TRUE;
try {
// Unset options unsupported by pm-update.
drush_unset_option('email');
// drush_invoke('up') always returns FALSE thus $return doesn't make sense.
$return = drush_invoke('up', $projects);
// Use Drush errors stack to check if update failed.
$errors = drush_get_context('DRUSH_ERROR_LOG', array());
if (!empty($errors)) {
foreach ($errors as $error_code => $error_messages) {
$messages[] = $error_code . ': ' . implode(', ', $error_messages);
}
$success = FALSE;
}
} catch (Exception $e) {
$messages[] = $e->getCode() . ': ' . $e->getMessage();
$success = FALSE;
}
// Restore robots.txt, .gitignore, .htaccess.
drush_print_r($candidates);
if (isset($candidates['drupal'])) {
$restore_files = array('robots.txt', '.gitignore', '.htaccess');
foreach ($restore_files as $file) {
$old_file = $backup_dir . '/drupal/' . $file;
$new_file = drush_get_context('DRUSH_DRUPAL_ROOT') . '/' . $file;
drush_print($old_file . ' ' . md5_file($old_file));
drush_print($new_file . ' ' . md5_file($new_file));
if (md5_file($old_file) != md5_file($new_file)) {
// Restore.
@drush_op('rename', $new_file, "$new_file.new");
copy($old_file, $new_file);
$messages[] = dt('Manual update of @file is required. New file is save as @file.new.', array('@file' => $file));
}
}
}
// Turn off maintenance mode.
drush_invoke_process('@self', 'vset', array(
'maintenance_mode',
'0'
), array('exact' => TRUE), FALSE);
// Disable Update module.
if ($update_is_enabled_by_script) {
drush_invoke_process('@self', 'dis', array('update'), array(), FALSE);
}
// Touch front page.
$site_available = TRUE;
$http_code = duuu_get_front_page_http_code();
drush_log(dt('Front page responses with @code code.', array('@code' => $http_code)));
if ($http_code >= 500) {
$success = FALSE;
$site_available = FALSE;
}
// Rollback.
if (!$success) {
// Call context-specific upc rollback function in case it wasn't called in drush_invoke('up').
drush_pm_updatecode_rollback();
// Database restore.
drush_invoke_process('@self', 'sql-query', array(), array('file' => $backup_sql), FALSE);
// Check if site was restored successfully.
$http_code = duuu_get_front_page_http_code();
if ($http_code >= 500) {
$site_available = FALSE;
}
}
$messages[] = dt('Backup is stored in @backup_dir', array('@backup_dir' => $backup_dir));
if (drush_shell_exec('which du')) {
drush_shell_exec("du -cksh %s", $backup_dir);
$return = drush_shell_exec_output();
if (isset($return[1]) && preg_match('/([\.\w]+)\stotal/', $return[1], $matches)) {
$messages[] = dt('Backup size: @size.', array('@size' => $matches[1]));
}
}
// 6. Send email.
drush_log(dt('6. Send email.'));
if ($success) {
$title = dt('Update successful to @candidates.', array('@candidates' => implode(', ', $candidates)));
}
else {
$title = dt('New releases available: @candidates. Automatic update failed.', array('@candidates' => implode(', ', $candidates)));
}
if (!$site_available) {
// TODO: does Drush still works if Drupal is down?
$title = dt('SITE UNAVAILABLE!') . ' ' . $title;
mail($email, $title, implode("\n", $messages), "Bcc: $bcc");
}
else {
$mail_message = drupal_mail('drush', 'key', $email, language_default(), array(), variable_get('site_mail'), FALSE);
$mail_message['subject'] = $title;
$mail_message['body'] = $messages;
$mail_message['headers']['Bcc'] = $bcc;
$system = drupal_mail_system('drush', 'key');
$mail_message = $system->format($mail_message);
$mail_message['result'] = $system->mail($mail_message);
$result = $mail_message['result'];
}
function duuu_get_front_page_http_code() {
$site_url = drush_get_context('DRUSH_URI');
if ($site_url != 'http://default' && valid_url($site_url) && drush_shell_exec('which curl')) {
drush_shell_exec('curl -s -o /dev/null -I -w "%{http_code}" ' . check_url($site_url) . '/');
$return = drush_shell_exec_output();
$http_code = intval($return[0]);
return $http_code;
}
// Command can not be executed.
return -1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment