? notthere.php ? includes/table.inc Index: commands/pm/pm.drush.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/drush/commands/pm/pm.drush.inc,v retrieving revision 1.97 diff -u -p -r1.97 pm.drush.inc --- commands/pm/pm.drush.inc 9 Mar 2010 02:07:01 -0000 1.97 +++ commands/pm/pm.drush.inc 16 Mar 2010 23:03:44 -0000 @@ -836,6 +836,14 @@ function drush_pm_update() { $args = func_get_args(); array_unshift($args, 'pm-updatecode'); call_user_func_array('drush_invoke', $args); + + // pm-updatecode will not do a core update of Drupal + // on the same invocation where non-core modules are + // updated. If there is a core update available, then + // call pm-updatecode a second time to update core. + if (drush_get_context('DRUSH_PM_CORE_UPDATE_AVAILABLE', FALSE)) { + call_user_func_array('drush_invoke', array('pm-updatecode', 'drupal')); + } } /** @@ -845,6 +853,7 @@ function drush_pm_update() { function drush_pm_post_pm_update() { // Use drush_backend_invoke to start a subprocess. Cleaner that way. drush_backend_invoke('updatedb'); + _drush_pm_reenable_modules(); } /** @@ -868,6 +877,9 @@ function drush_pm_post_pm_updatecode() { } } } + + // If we automatically disabled modules, warn the user + _drush_pm_disabled_modules_warning(); } Index: commands/pm/updatecode.pm.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/drush/commands/pm/updatecode.pm.inc,v retrieving revision 1.6 diff -u -p -r1.6 updatecode.pm.inc --- commands/pm/updatecode.pm.inc 6 Mar 2010 13:58:14 -0000 1.6 +++ commands/pm/updatecode.pm.inc 16 Mar 2010 23:03:44 -0000 @@ -89,8 +89,34 @@ function drush_pm_updatecode() { } if (isset($updateable['drupal'])) { - drush_print("NOTE: A code update for the Drupal core is available. \nDrupal itself can't yet be updated by this tool. Please update Drupal manually.\n"); + $drupal_project = $updateable['drupal']; + unset($projects['drupal']); unset($updateable['drupal']); + $module_list = array_keys($projects); + + // We can only upgrade drupal core if there are no non-core + // modules enabled. _pm_update_core will disable the + // modules passed in, and insure that they are enabled again + // when we're done. However, each run of pm-updatecode will + // update -either- core, or the non-core modules; never both. + // This simplifies rollbacks. + if (empty($updateable)) { + return _pm_update_core($drupal_project, $module_list); + } + // If there are modules other than drupal core enabled, then go + // ahead and print instructions on how to upgrade core. We will + // also continue, allowing the user to upgrade any upgradable + // modules if desired. + else { + drush_print(dt("NOTE: A code update for the Drupal core is available. \nIn order to update Drupal core with this tool, first disable all of the modules listed above and then run pm-updatecode again.\n")); + drush_print(dt("e.g.:\n")); + drush_print(dt(" drush pm-disable !modules", array("!modules" => implode(' ',$module_list)))); + drush_print(dt(" drush pm-updatecode")); + drush_print(dt(" drush updatedb")); + drush_print(dt(" drush pm-enable !modules\n", array("!modules" => implode(' ',$module_list)))); + drush_print(dt("\nAlternately, run 'pm-updatecode drupal' to upgrade Drupal core only.")); + drush_set_context('DRUSH_PM_CORE_UPDATE_AVAILABLE', TRUE); + } } if (empty($updateable)) { @@ -102,6 +128,78 @@ function drush_pm_updatecode() { } /** + * Update drupal core, following interactive confirmation from the user. + * + * @param $project + * The drupal project information from the drupal.org update service, + * copied from $projects['drupal']. @see drush_pm_updatecode. + * @param $module_list + * A list of the non-core modules that are enabled. These must be disabled + * before core can be updated. + */ +function _pm_update_core($project, $module_list = array()) { + drush_include_engine('package_handler', drush_get_option('package-handler', 'wget')); + $drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT'); + + drush_print(dt('Code updates will be made to drupal core.')); + drush_print(dt("Note: Updating core can potentially break your site. It is NOT recommended to update production sites without prior testing.")); + if (!empty($module_list)) { + drush_print(dt("Prior to updating core, the following modules will be disabled: !modules", array("!modules" => implode(' ',$module_list)))); + drush_print(dt("They will be re-enabled after the update is complete.")); + } + if(!drush_confirm(dt('Do you really want to continue?'))) { + drush_die('Aborting.'); + } + + // If there are any non-core modules enabled, disable them + if (!empty($module_list)) { + drush_set_context('DRUSH_PM_DISABLED_MODULES', $module_list); + drush_backend_invoke_args('pm-disable', $module_list, array('y' => TRUE)); + } + + // Create a directory 'core' if it does not already exist + $project['path'] = 'drupal-' . $project['candidate_version']; + $project['full_project_path'] = $drupal_root . '/' . $project['path']; + mkdir($project['full_project_path']); + + // Create a list of files and folders that are user-customized or otherwise + // not part of the update process + $project['skip_list'] = array('.htaccess', 'robots.txt', 'backup', 'sites', $project['path']); + + // Move all files and folders in $drupal_root to the new 'core' directory + // except for the items in the skip list + _pm_update_move_files($drupal_root, $project['full_project_path'], $project['skip_list']); + + // Update core. + $version_control = pm_update_project($project); + if ($version_control === FALSE) { + return FALSE; + } + + // Take the updated files in the 'core' directory that have been updated, + // and move all except for the items in the skip list back to + // the drupal root + _pm_update_move_files($project['full_project_path'], $drupal_root, $project['skip_list']); + drush_delete_dir($project['full_project_path']); + + pm_update_complete($project, $version_control); + + return TRUE; +} + +/** + * Move some files from one location to another + */ +function _pm_update_move_files($src_dir, $dest_dir, $skip_list) { + $items_to_move = drush_scan_directory($src_dir, '/.*/', array_merge(array('.', '..', 'CVS'), $skip_list), 0, FALSE); + foreach ($items_to_move as $filename => $info) { + drush_delete_dir($dest_dir . '/' . basename($filename)); + $move_result = rename($filename, $dest_dir . '/' . basename($filename)); + } + return TRUE; +} + +/** * Update packages according to an array of releases, following interactive * confirmation from the user. * @@ -127,7 +225,6 @@ function pm_update_packages($projects) { } // Now we start the actual updating. - $updated = array(); foreach($projects as $project) { if (empty($project['path'])) { return drush_set_error('DRUSH_PM_UPDATING_NO_PROJECT_PATH', dt('The !project project path is not available, perhaps the !type is enabled but has been deleted from disk.', array('!project' => $project['name'], '!type' => $project['project_type']))); @@ -135,36 +232,58 @@ function pm_update_packages($projects) { drush_log(dt('Starting to update !project code at !dir...', array('!project' => $project['title'], '!dir' => $project['path']))); // Create the projects directory and base (parent) directory. $project['full_project_path'] = $drupal_root . '/' . $project['path']; - $project['base_project_path'] = dirname($project['full_project_path']); // Check that the directory exists, and is where we expect it to be. if (stripos($project['path'], $project['project_type']) === FALSE || !is_dir($project['full_project_path'])) { return drush_set_error('DRUSH_PM_UPDATING_PATH_NOT_FOUND', dt('The !project directory could not be found within the !types directory at !full_project_path, perhaps the project is enabled but has been deleted from disk.', array('!project' => $project['name'], '!type' => $project['project_type'], '!full_project_path' => $project['full_project_path']))); } - - // Check we have a version control system, and it clears it's pre-flight. - if (!$version_control = drush_pm_include_version_control($project['full_project_path'])) { + // Run update on one project + $version_control = pm_update_project($project); + if ($update_status === FALSE) { return FALSE; } - if (!$version_control->pre_update($project)) { - return FALSE; - } - // Add the project to a context so we can roll back if needed. - $updated[] = $project; - drush_set_context('DRUSH_PM_UPDATED', $updated); - - if (!package_handler_update_project($project, $project['releases'][$project['candidate_version']])) { - return drush_set_error('DRUSH_PM_UPDATING_FAILED', dt('Updating project !project failed. Attempting to roll back to previously installed version.', array('!project' => $project['name']))); - } - else { - drush_print(dt('Project !project was updated successfully. Installed version is now !version.', array('!project' => $project['name'], '!version' => $project['candidate_version']))); - drush_command_invoke_all('pm_post_update', $project['name'], $project['releases'][$project['candidate_version']]); - $version_control->post_update($project); - } + pm_update_complete($project, $version_control); } // Clear the cache, since some projects could have moved around. drush_drupal_cache_clear_all(); } +/** + * Update one project -- a module, theme or Drupal core + * + * @param $project + * The project to upgrade. $project['full_project_path'] must be set + * to the location where this project is stored. + */ +function pm_update_project($project) { + $project['base_project_path'] = dirname($project['full_project_path']); + // Check we have a version control system, and it clears it's pre-flight. + if (!$version_control = drush_pm_include_version_control($project['full_project_path'])) { + return FALSE; + } + if (!$version_control->pre_update($project)) { + return FALSE; + } + // Add the project to a context so we can roll back if needed. + $updated = drush_get_context('DRUSH_PM_UPDATED'); + $updated[] = $project; + drush_set_context('DRUSH_PM_UPDATED', $updated); + + if (!package_handler_update_project($project, $project['releases'][$project['candidate_version']])) { + return drush_set_error('DRUSH_PM_UPDATING_FAILED', dt('Updating project !project failed. Attempting to roll back to previously installed version.', array('!project' => $project['name']))); + } + + return $version_control; +} + +/** + * Run the post-update hooks after updatecode is complete for one project. + */ +function pm_update_complete($project, $version_control) { + drush_print(dt('Project !project was updated successfully. Installed version is now !version.', array('!project' => $project['name'], '!version' => $project['candidate_version']))); + drush_command_invoke_all('pm_post_update', $project['name'], $project['releases'][$project['candidate_version']]); + $version_control->post_update($project); +} + function drush_pm_updatecode_rollback() { $projects = array_reverse(drush_get_context('DRUSH_PM_UPDATED', array())); foreach($projects as $project) { @@ -175,6 +294,30 @@ function drush_pm_updatecode_rollback() return FALSE; } $version_control->rollback($project); + + // Post rollback, we will do additional repair if the project is drupal core. + if ($project['name'] == 'drupal') { + $drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT'); + _pm_update_move_files($project['full_project_path'], $drupal_root, $project['skip_list']); + drush_delete_dir($project['full_project_path']); + } + } + _drush_pm_reenable_modules(); +} + +function _drush_pm_reenable_modules() { + $modules = drush_get_context('DRUSH_PM_DISABLED_MODULES', array()); + if (!empty($modules)) { + drush_backend_invoke_args('pm-enable', $modules, array('y' => TRUE)); + } +} + +function _drush_pm_disabled_modules_warning() { + $modules = drush_get_context('DRUSH_PM_DISABLED_MODULES', array()); + if (!empty($modules)) { + drush_print(dt("n.b.: 'pm-updatecode drupal' disabled some modules. Re-enable them after running updatedb:")); + drush_print(dt(" drush updatedb")); + drush_print(dt(" drush pm-enable !modules\n", array("!modules" => implode(' ',$modules)))); } } Index: includes/drush.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/drush/includes/drush.inc,v retrieving revision 1.96 diff -u -p -r1.96 drush.inc --- includes/drush.inc 6 Mar 2010 12:46:30 -0000 1.96 +++ includes/drush.inc 16 Mar 2010 23:03:44 -0000 @@ -472,6 +472,28 @@ function _drush_delete_registered_files( } /** + * Deletes the provided file or folder and + * everything inside it. + */ +function drush_delete_dir($dir) { + if (!file_exists($dir)) { + return TRUE; + } + if (!is_dir($dir)) { + return unlink($dir); + } + foreach (scandir($dir) as $item) { + if ($item == '.' || $item == '..') { + continue; + } + if (!drush_delete_dir($dir.DIRECTORY_SEPARATOR.$item)) { + return FALSE; + } + } + return rmdir($dir); +} + +/** * Calls a given function, passing through all arguments unchanged. * * This should be used when calling possibly mutative or destructive functions