function drush_upgrade_site_upgrade

3.x upgrade.drush.inc drush_upgrade_site_upgrade($target_key)
4.x upgrade.drush.inc drush_upgrade_site_upgrade($target_key)

Main command hook for site-upgrade.

This runs bootstrapped to the SOURCE site.

File

commands/core/upgrade.drush.inc, line 100
Refine your Drupal major version upgrade.

Code

function drush_upgrade_site_upgrade($target_key) {
  // Presume we are ready to go (n.b. some checks already performed in 'validate')
  $ready_to_upgrade = TRUE;
  $result = TRUE;

  // PREPARE:  Find the target version and determine the contrib projects and enabled modules installed.

  $source_version = drush_drupal_major_version();
  $target_version = $source_version + 1;
  $target_alias = drush_sitealias_get_record($target_key);
  if (empty($target_alias)) {
    return drush_set_error('DRUSH_UPGRADE_NO_TARGET', dt("Could not find target site for upgrade: !target", array("!target" => $target_key)));
  }

  $destination_core = $target_alias['root'];

  $destination_conf_path = conf_path();
  if (drush_get_option('force-sites-default')) {
    $destination_conf_path = 'sites/default';
  }

  // Get a list of enabled contrib extensions.
  $values = drush_invoke_process_args('pm-list', array(), array('status' => 'enabled', 'no-core' => TRUE, '#integrate' => FALSE, '#override-simulated' => TRUE));
  if ($values['error_status'] != 0) {
    return FALSE;
  }
  $contrib_extensions = array_keys($values['object']);

  // Get a list of enabled core extensions.
  $values = drush_invoke_process_args('pm-list', array(), array('status' => 'enabled', 'core' => TRUE, '#integrate' => FALSE, '#override-simulated' => TRUE));
  if ($values['error_status'] != 0) {
    return FALSE;
  }
  $core_extensions = array_keys($values['object']);

  // Make a list of modules that are not available to be enabled in the target
  // version of Drupal, either because they have not been ported yet, or because
  // they have been rolled into core.
  $unavailable_extensions = array();

  // Get the list of modules the user would like to uninstall (if any).
  $uninstall_extensions = drush_get_option('uninstall', '');
  if ($uninstall_extensions == "all") {
    $uninstall_extensions = $contrib_extensions;
  }
  else {
    $uninstall_extensions = explode(',', $uninstall_extensions);
  }

  // WARN:  Tell the user about any special situations that might exist with contrib modules.

  $project_download_list = array();
  $extension_info = drush_pm_get_extensions();
  if (!empty($contrib_extensions) && !drush_get_option('core-only')) {
    // Make a list of all of the extensions to download.  We will
    // download everything in the contrib extension list, but we
    // will skip projects that already exist.
    $special_projects = drush_upgrade_project_map($target_version);
    $in_core = array();
    $special_warning = array();
    foreach ($contrib_extensions as $extension_name) {
      // Only check extensions that are NOT in our uninstall list.
      if (!in_array($extension_name, $uninstall_extensions) && array_key_exists($extension_name, $extension_info)) {
        $project = $extension_info[$extension_name]->info['project'];
        // Check our lookup table to see if a project has been renamed.
        if (array_key_exists($project, $special_projects['project-remap'])) {
          $project = $special_projects['project-remap'][$project];
        }
        // If the module has been rolled into core in the next major release of
        // Drupal, then we do not need to download it.  Add it to an array for
        // reporting purposes.
        if ((in_array($project, $special_projects['projects-in-core'])) || (in_array($extension_name, $special_projects['modules-in-core']))) {
          $in_core[$extension_name] = $extension_name;
          // Might some of these need to be enabled?
          $unavailable_extensions[] = $extension_name;
        }
        elseif (($extension_info[$extension_name]->type == 'module') && !is_dir($destination_core . '/sites/all/modules/' . $project) && !is_dir($destination_core . '/' . $destination_conf_path . '/modules/' . $project)) {
          $project_download_list[$project][] = $extension_name;
        }
        // If there is a special warning about a project, then add it 
        // to the warning list for later reporting.
        if (array_key_exists($project . ':' . $extension_name, $special_projects['warning'])) {
          $special_warning[$project] = $special_projects['warning'][$project . ':' . $extension_name];
        }
        elseif ((array_key_exists($project, $special_projects['warning'])) && (!array_key_exists($project, $special_warning))) {
          $special_warning[$project] = $special_projects['warning'][$project];
        }
      }
    }

    // Consider each project from the contrib extensions and check with PM to see if there is
    // a recommended release available to download.  If there is NO release available, then
    // we are not ready to upgrade (but still can, without that project); if there is no RECOMMENDED 
    // release, then we might not be ready to upgrade (but still can, with a non-recommended release).
    if (!empty($project_download_list)) {
      $result = drush_invoke_process('@none', 'pm-releases', array_keys($project_download_list), array('default-major' => $target_version, '#integrate' => FALSE, '#override-simulated' => TRUE));
      $project_releases = $result['object'];
      foreach ($project_download_list as $project => $extension_list) {
        if (!array_key_exists($project, $project_releases)) {
          drush_log(dt('The project !project has no releases in version !version', array('!project' => $project, '!version' => $target_version)), 'warning');
          $unavailable_extensions = array_merge($unavailable_extensions, $extension_list);
          $ready_to_upgrade = FALSE;
        }
        else {
          if (empty($project_releases[$project]['recommended'])) {
            drush_log(dt('The project !project has no recommended release in version !version', array('!project' => $project, '!version' => $target_version)), 'warning');
            $ready_to_upgrade = 'maybe';
          }
        }
      }
    }

    // Print out some messages about projects that migrated  to core, or modules that will require special processing.
    if (!empty($in_core)) {
      drush_log(dt('The following contrib modules were enabled in your Drupal site, but are now standard in core: !in_core.  These modules may need to be reconfigured after the upgrade is complete.', array('!in_core' => implode(', ', $in_core))), 'ok');
    }
    foreach ($special_warning as $project => $warning) {
      if ($warning === TRUE) {
        $warning = 'Please see !project_page and !source for more information on how to do this.';
      }
      if ($warning === FALSE) {
        $warning = 'So far there is no indication of when a migration path will be provided.  Check !project_page for updates.';
        $ready_to_upgrade = 'maybe';
      }
      drush_log(dt("You are using the project !project, which requires data migration or other special processing.  $warning", array('!project' => $project, '!project_page' => 'http://drupal.org/project/' . $project, '!source' => $special_projects['source'])), 'warning');
    }
  }

  // CONFIRM:  Ask the user before overwriting an exsiting site, and ask if an upgrade is really decided if the site is not ready yet.

  // Summarize whether or not there is a good chance that the site can be upgraded.
  if ($ready_to_upgrade !== TRUE) {
    drush_log(dt("Based on the contrib modules enabled in this site, it is possible that the site-upgrade command might fail.  See warnings above."), (($ready_to_upgrade === FALSE) ? 'warning' : 'notice'));
  }
  // Check to see what we should do if the target Drupal folder already exists.
  $options = array(
    'replace' => dt("Delete the existing site and start over."),
    'reuse' => dt("Re-use the existing code, re-import the database from the source site and run updatedb again."),
  );
  $selection = NULL;
  foreach ($options as $option => $msg) {
    if (drush_get_option($option, FALSE)) {
      $selection = $option;
    }
  }
  if (!isset($selection) && (file_exists($destination_core))) {
    $selection = drush_choice($options, dt("Drupal site already exists at !root.  Would you like to:", array('!root' => $destination_core)));
    if (!$selection) {
      return drush_user_abort();
    }
  }
  elseif ($ready_to_upgrade !== TRUE) {
    if (!drush_confirm('Would you like to continue anyway?')) {
      return drush_user_abort();
    }
  }

  // User has already been prompted; skip further confirms.
  drush_set_context('DRUSH_AFFIRMATIVE', TRUE);

  // We need to know where our destination settings file is regardless of which
  // code path we take; therefore, we will precompute it here.

  $settings_destination = $destination_core . '/' . $destination_conf_path . '/settings.php';

  // STEP 1:  Download the next major version of Drupal.

  if (($selection == 'replace') || (!is_dir($destination_core))) {
    drush_upgrade_download_drupal($target_version, $destination_core);
    if (drush_get_error()) {
      return FALSE; // Early exit if we see an error.
    }
    drush_upgrade_copy_settings($target_alias, $settings_destination);
  }
  else {
    // Move sites/all/modules and $conf_path()/modules out of the way
    // so that updatedb can be run on core only.
    if (_drush_upgrade_preserve_modules($destination_core) === FALSE) {
      return FALSE;
    }
  }

  // Copy source database to target database. The source DB is not changed.
  // Always set 'common' at minimum. Sites that want other can create other key in drushrc.php.
  if (!drush_get_option('structure-tables-key')) {
    drush_set_option('structure-tables-key', 'common');
  }
  // Always blow away the target database so we start fresh.
  // We still have DRUSH_AFFIRMATIVE set from above, so this will not prompt.
  drush_set_option('create-db', TRUE);
  drush_include(DRUSH_BASE_PATH . '/commands/sql', 'sync.sql');
  drush_invoke('sql-sync', '@self', $target_key);
  if (drush_get_error()) {
    return FALSE; // Early exit if we see an error.
  }

  if (!empty($contrib_extensions)) {
    $target_alias_databases = sitealias_get_databases_from_record($target_alias);
    $modify_site_conf_path = NULL;

    // Make an alias record that uses the CODE from @self and the DATABASE from $target.
    // Since we just did an sql-sync from @self to @target, we can use this hybrid specification
    // to do manipulations on the target database before runing updatedb.  In brief, we are going
    // to disable all contrib modules to prevent problems with updatedb.
    $modify_site = array(
      'root' => DRUPAL_ROOT,
      'uri' => $target_alias_databases['default']['default']['database'],
    );

    if (!drush_get_context('DRUSH_SIMULATE')) {
      // In theory, if the sql-sync worked, this should never be empty.
      if (empty($modify_site['uri'])) {
        return drush_set_error('DRUSH_UPGRADE_DATABASE_SPEC_UNKNOWN', dt('Failed to look up database spec for @target', array('@target' => $target_key)));
      }
      $modify_site_conf_path = dirname(conf_path()) . '/' . $modify_site['uri'];
      $modify_site_settings = $modify_site_conf_path . '/settings.php';
      drush_log('set up a fake site by copying ' . $settings_destination . ' to ' . $modify_site_settings);
      if (!file_exists($modify_site_settings)) {
        if ((drush_mkdir($modify_site_conf_path) === FALSE) || drush_op('copy', $settings_destination, $modify_site_settings) !== TRUE) {
          return drush_set_error('DRUSH_UPGRADE_COULD_NOT_DISABLE', dt("Could not create a temporary multisite "));
        }
      }
    }
    $result = (drush_invoke_sitealias_args($modify_site, 'site-upgrade-prepare', $contrib_extensions, array('uninstall' => implode(',', $uninstall_extensions), 'yes' => TRUE, '#interactive' => TRUE)) == 0);

    // Delete the temporary site now that we're done with it.
    if (isset($modify_site_conf_path)) {
      drush_delete_dir($modify_site_conf_path);
    }
    if ($result === FALSE) {
      return FALSE;
    }
  }

  // STEP 2:  Call updatedb for Drupal core.

  // Run update.php in a subshell. It is run on @target site whereas this request was on @self.
  drush_log(dt('About to perform updatedb for Drupal core on !target', array('!target' => $target_key)), 'ok');
  // When we call drush_invoke_sitealias_args in #interactive mode, the result code comes from drush_op_system, where 0 == success.
  $result = drush_invoke_sitealias_args($target_alias, 'updatedb', array(), array('yes' => TRUE, '#interactive' => TRUE)) == 0;
  if ($result === FALSE) {
    return drush_set_error('DRUSH_DRUPAL_CORE_UPGRADE_FAILED', dt("The call to updatedb failed for Drupal core.  This may be caused by a contrib module that is not yet ready for upgrade.  Try running site-upgrade again with '--uninstall={module list}' to remove all contrib extensions prior to upgrade.  Add modules back in until the problematic one is isolated.  Please report problems in the issue queue of the module that is causing problems."));
  }
  drush_log(dt('updatedb complete for Drupal core'), 'ok');

  // If we moved our modules out of the way, bring them back now.
  _drush_upgrade_restore_preserved_modules();

  // STEP 3: Download and re-enable the contrib modules.

  if (!empty($contrib_extensions) && !drush_get_option('core-only')) {
    $options = array('#interactive' => TRUE);
    if (!empty($project_download_list)) {
      $projects = implode(',', array_keys($project_download_list));
      $options['projects'] = $projects;
    }
    // If a module changed name, then rename it prior to calling pm-enable.
    foreach ($contrib_extensions as $extension_name) {
      if (array_key_exists($extension_name, $special_projects['module-remap'])) {
        $unavailable_extensions[] = $extension_name;
        $contrib_extensions[] = $special_projects['module-remap'][$extension_name];
      }
    }

    // Redispatch to site-upgrade-modules command, so that we will be
    // bootstrapped to the target site.
    $result = (drush_invoke_sitealias_args($target_alias, 'site-upgrade-modules', array_merge($core_extensions, array_diff($contrib_extensions, $unavailable_extensions, $uninstall_extensions)), $options) == 0);
  }

  return $result;
}