Automatic Updates: Add a rollback functionality upon installation failure, the rollback package will be available for partial-updates for automatic updates and be similar to our existing partial builds (but in reverse).

A further iteration of this is to also detect whitescreens (fatals) after a auto update, and trigger the rollback for that too.
See #22704

Built from https://develop.svn.wordpress.org/trunk@25649


git-svn-id: http://core.svn.wordpress.org/trunk@25566 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Dion Hulse 2013-09-30 00:21:09 +00:00
parent 7ed4059230
commit 75475ed773
4 changed files with 28 additions and 7 deletions

View File

@ -1224,6 +1224,8 @@ class Core_Upgrader extends WP_Upgrader {
$this->strings['unpack_package'] = __('Unpacking the update…'); $this->strings['unpack_package'] = __('Unpacking the update…');
$this->strings['copy_failed'] = __('Could not copy files.'); $this->strings['copy_failed'] = __('Could not copy files.');
$this->strings['copy_failed_space'] = __('Could not copy files. You may have run out of disk space.' ); $this->strings['copy_failed_space'] = __('Could not copy files. You may have run out of disk space.' );
$this->strings['start_rollback'] = __( 'Attempting to rollback to previous version.' );
$this->strings['rollback_was_required'] = __( 'Due to an error during updating, WordPress has rolled back to your previous version' );
} }
function upgrade( $current, $args = array() ) { function upgrade( $current, $args = array() ) {
@ -1231,6 +1233,8 @@ class Core_Upgrader extends WP_Upgrader {
$defaults = array( $defaults = array(
'pre_check_md5' => true, 'pre_check_md5' => true,
'attempt_rollback' => false,
'do_rollback' => false,
); );
$parsed_args = wp_parse_args( $args, $defaults ); $parsed_args = wp_parse_args( $args, $defaults );
@ -1251,14 +1255,18 @@ class Core_Upgrader extends WP_Upgrader {
get_core_checksums( array( $wp_version, $current->version ) ); get_core_checksums( array( $wp_version, $current->version ) );
$no_partial = false; $no_partial = false;
if ( $parsed_args['pre_check_md5'] && ! $this->check_files() ) if ( $parsed_args['do_rollback'] )
$no_partial = true;
elseif ( $parsed_args['pre_check_md5'] && ! $this->check_files() )
$no_partial = true; $no_partial = true;
// If partial update is returned from the API, use that, unless we're doing a reinstall. // If partial update is returned from the API, use that, unless we're doing a reinstall.
// If we cross the new_bundled version number, then use the new_bundled zip. // If we cross the new_bundled version number, then use the new_bundled zip.
// Don't though if the constant is set to skip bundled items. // Don't though if the constant is set to skip bundled items.
// If the API returns a no_content zip, go with it. Finally, default to the full zip. // If the API returns a no_content zip, go with it. Finally, default to the full zip.
if ( $current->packages->partial && 'reinstall' != $current->response && $wp_version == $current->partial_version && ! $no_partial ) if ( $parsed_args['do_rollback'] && $current->packages->rollback )
$to_download = 'rollback';
elseif ( $current->packages->partial && 'reinstall' != $current->response && $wp_version == $current->partial_version && ! $no_partial )
$to_download = 'partial'; $to_download = 'partial';
elseif ( $current->packages->new_bundled && version_compare( $wp_version, $current->new_bundled, '<' ) elseif ( $current->packages->new_bundled && version_compare( $wp_version, $current->new_bundled, '<' )
&& ( ! defined( 'CORE_UPGRADE_SKIP_NEW_BUNDLED' ) || ! CORE_UPGRADE_SKIP_NEW_BUNDLED ) ) && ( ! defined( 'CORE_UPGRADE_SKIP_NEW_BUNDLED' ) || ! CORE_UPGRADE_SKIP_NEW_BUNDLED ) )
@ -1283,12 +1291,22 @@ class Core_Upgrader extends WP_Upgrader {
} }
$wp_filesystem->chmod($wp_dir . 'wp-admin/includes/update-core.php', FS_CHMOD_FILE); $wp_filesystem->chmod($wp_dir . 'wp-admin/includes/update-core.php', FS_CHMOD_FILE);
require(ABSPATH . 'wp-admin/includes/update-core.php'); require_once( ABSPATH . 'wp-admin/includes/update-core.php' );
if ( ! function_exists( 'update_core' ) ) if ( ! function_exists( 'update_core' ) )
return new WP_Error( 'copy_failed_space', $this->strings['copy_failed_space'] ); return new WP_Error( 'copy_failed_space', $this->strings['copy_failed_space'] );
$result = update_core( $working_dir, $wp_dir ); $result = update_core( $working_dir, $wp_dir, $parsed_args['do_rollback'] );
// In the event of an error, rollback to the previous version
if ( is_wp_error( $result ) && $parsed_args['attempt_rollback'] && $current->packages->rollback ) {
apply_filters( 'update_feedback', $result );
apply_filters( 'update_feedback', $this->strings['start_rollback'] );
$this->upgrade( $current, array_merge( $parsed_args, array( 'do_rollback' => true ) ) );
$result = new WP_Error( 'rollback_was_required', $this->strings['rollback_was_required'] );
}
do_action( 'upgrader_process_complete', $this, array( 'action' => 'update', 'type' => 'core' ), $result ); do_action( 'upgrader_process_complete', $this, array( 'action' => 'update', 'type' => 'core' ), $result );
return $result; return $result;
} }
@ -1630,6 +1648,7 @@ class WP_Automatic_Upgrader {
$upgrade_result = $upgrader->upgrade( $item, array( $upgrade_result = $upgrader->upgrade( $item, array(
'clear_update_cache' => false, 'clear_update_cache' => false,
'pre_check_md5' => false, /* always use partial builds if possible for core updates */ 'pre_check_md5' => false, /* always use partial builds if possible for core updates */
'attempt_rollback' => true, /* only available for core updates */
) ); ) );
// Core doesn't output this, so lets append it so we don't get confused // Core doesn't output this, so lets append it so we don't get confused

View File

@ -665,7 +665,7 @@ function update_core($from, $to) {
} }
$wp_filesystem->chmod( $versions_file, FS_CHMOD_FILE ); $wp_filesystem->chmod( $versions_file, FS_CHMOD_FILE );
require_once( WP_CONTENT_DIR . '/upgrade/version-current.php' ); require( WP_CONTENT_DIR . '/upgrade/version-current.php' );
$wp_filesystem->delete( $versions_file ); $wp_filesystem->delete( $versions_file );
$php_version = phpversion(); $php_version = phpversion();

View File

@ -136,6 +136,8 @@ function get_core_checksums( $version ) {
// Cache the checksums for later // Cache the checksums for later
foreach ( $version as $v ) { foreach ( $version as $v ) {
if ( ! isset( $body['checksums'][ $v ] ) )
$body['checksums'][ $v ] = false;
set_site_transient( "core_checksums_$v", $body['checksums'][ $v ], HOUR_IN_SECONDS ); set_site_transient( "core_checksums_$v", $body['checksums'][ $v ], HOUR_IN_SECONDS );
$return[ $v ] = $body['checksums'][ $v ]; $return[ $v ] = $body['checksums'][ $v ];
} }

View File

@ -105,7 +105,7 @@ function wp_version_check() {
foreach ( $offer as $offer_key => $value ) { foreach ( $offer as $offer_key => $value ) {
if ( 'packages' == $offer_key ) if ( 'packages' == $offer_key )
$offer['packages'] = (object) array_intersect_key( array_map( 'esc_url', $offer['packages'] ), $offer['packages'] = (object) array_intersect_key( array_map( 'esc_url', $offer['packages'] ),
array_fill_keys( array( 'full', 'no_content', 'new_bundled', 'partial' ), '' ) ); array_fill_keys( array( 'full', 'no_content', 'new_bundled', 'partial', 'rollback' ), '' ) );
elseif ( 'download' == $offer_key ) elseif ( 'download' == $offer_key )
$offer['download'] = esc_url( $value ); $offer['download'] = esc_url( $value );
else else