Drupal and Drush: Updating the database from the command-line

So now that we’re doing all our configuration changes in source code, it makes sense to automate database updates as much as I can. Here’s something I’ve added to drush_tools so that I can run all the schema changes from the command-line:

function drush_tools_update($command = '') {
  ob_start();
  require_once 'includes/install.inc';
  require_once 'update.php';
  $ret = ob_get_contents();
  drupal_load_updates();
  ob_end_clean();

  $list = module_list();
  $update_list = array();
  foreach ($list as $module) {
    $updates = drupal_get_schema_versions($module);
    if ($updates !== FALSE) {
      $latest = 0;
      $base = drupal_get_installed_schema_version($module);
      foreach ($updates as $update) {
        if ($update > $base) {
          if ($update > $latest) { $latest = $update; }
          $update_list[$module][] = $update;
        }
      }
      if ($latest) {
        sort($update_list[$module]);
        printf("%-30s %5d -> %5d (%s)\n", $module, $base, $latest, join(', ', $update_list[$module]));
      } else {
        printf("%-30s %5d\n", $module, $base);
      } 
    }      
  }
  if (count($update_list) == 0) return;
  if ($command != 'force' && !drush_confirm(t('Do you really want to continue?'))) {
    drush_die('Aborting.');
  }
  ob_start();
  foreach ($update_list as $module => $versions) {
    foreach ($versions as $v) {
      print "Running " . $module . "_update_" . $v . "\n";
      update_data($module, $v);
    }
  }
  $updates = ob_get_contents();

  cache_clear_all('*', 'cache', TRUE);
  cache_clear_all('*', 'cache_page', TRUE);
  cache_clear_all('*', 'cache_menu', TRUE);
  cache_clear_all('*', 'cache_filter', TRUE);
  drupal_clear_css_cache();
  ob_end_clean();
  $output = '';
  if (!empty($_SESSION['update_results'])) {
    $output .= "The following queries were executed:\n";
    foreach ($_SESSION['update_results'] as $module => $updates) {
      $output .= "\n" . $module . "\n--------------------------\n";
      foreach ($updates as $number => $queries) {
        $output .= 'Update #'. $number . ":\n";
        foreach ($queries as $query) {
          if ($query['success']) {
            $output .= "SUCCESS: " . $query['query'] . "\n";
          }
          else {
            $output .= "FAILURE: " . $query['query'] . "\n";
          }
        }
        if (!count($queries)) {
          $output .= "No queries\n";
        }
      }
    }
    $output .= "\n";
    print $output;
    unset($_SESSION['update_results']);
  }
}