Skip to content

Commit dcca932

Browse files
Upgrade/Install: Create a temporary backup of plugins and themes before updating.
This aims to make the update process more reliable and ensures that if a plugin or theme update fails, the previous version can be safely restored. * When updating a plugin or theme, the old version is moved to a temporary backup directory: * `wp-content/upgrade/temp-backup/plugins/[plugin-slug]` for plugins * `wp-content/upgrade/temp-backup/themes/[theme-slug]` for themes. * If the update fails, then the temporary backup kept in the `upgrade/temp-backup` directory is restored to its original location. * If the update succeeds, the temporary backup is deleted. To further help troubleshoot plugin and theme updates, two new checks were added to the Site Health screen: * A check to make sure that the `temp-backup` directory is writable. * A check that there is enough disk space available to safely perform updates. To avoid confusion: The `temp-backup` directory will NOT be used to "roll back" a plugin to a previous version after a completed update. This directory will simply contain a transient backup of the previous version of a plugin or theme being updated, and as soon as the update process finishes, the directory will be empty. Props aristath, afragen, pbiron, dd32, poena, TimothyBlynJacobs, audrasjb, mikeschroder, a2hosting, hellofromTonya, KZeni, galbaras, richards1052, Boniu91, mai21, francina, SergeyBiryukov. See #51857. Built from https://develop.svn.wordpress.org/trunk@51815 git-svn-id: http://core.svn.wordpress.org/trunk@51422 1a063a9b-81f0-0310-95a4-ce76da25c4cd
1 parent a13f7b3 commit dcca932

File tree

5 files changed

+431
-25
lines changed

5 files changed

+431
-25
lines changed

wp-admin/includes/class-plugin-upgrader.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -226,9 +226,14 @@ public function upgrade( $plugin, $args = array() ) {
226226
'clear_destination' => true,
227227
'clear_working' => true,
228228
'hook_extra' => array(
229-
'plugin' => $plugin,
230-
'type' => 'plugin',
231-
'action' => 'update',
229+
'plugin' => $plugin,
230+
'type' => 'plugin',
231+
'action' => 'update',
232+
'temp_backup' => array(
233+
'slug' => dirname( $plugin ),
234+
'src' => WP_PLUGIN_DIR,
235+
'dir' => 'plugins',
236+
),
232237
),
233238
)
234239
);
@@ -342,7 +347,12 @@ public function bulk_upgrade( $plugins, $args = array() ) {
342347
'clear_working' => true,
343348
'is_multi' => true,
344349
'hook_extra' => array(
345-
'plugin' => $plugin,
350+
'plugin' => $plugin,
351+
'temp_backup' => array(
352+
'slug' => dirname( $plugin ),
353+
'src' => WP_PLUGIN_DIR,
354+
'dir' => 'plugins',
355+
),
346356
),
347357
)
348358
);

wp-admin/includes/class-theme-upgrader.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -328,9 +328,14 @@ public function upgrade( $theme, $args = array() ) {
328328
'clear_destination' => true,
329329
'clear_working' => true,
330330
'hook_extra' => array(
331-
'theme' => $theme,
332-
'type' => 'theme',
333-
'action' => 'update',
331+
'theme' => $theme,
332+
'type' => 'theme',
333+
'action' => 'update',
334+
'temp_backup' => array(
335+
'slug' => $theme,
336+
'src' => get_theme_root( $theme ),
337+
'dir' => 'themes',
338+
),
334339
),
335340
)
336341
);
@@ -443,7 +448,12 @@ public function bulk_upgrade( $themes, $args = array() ) {
443448
'clear_working' => true,
444449
'is_multi' => true,
445450
'hook_extra' => array(
446-
'theme' => $theme,
451+
'theme' => $theme,
452+
'temp_backup' => array(
453+
'slug' => $theme,
454+
'src' => get_theme_root( $theme ),
455+
'dir' => 'themes',
456+
),
447457
),
448458
)
449459
);

wp-admin/includes/class-wp-site-health.php

Lines changed: 215 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1882,6 +1882,196 @@ public function get_test_plugin_theme_auto_updates() {
18821882
return $result;
18831883
}
18841884

1885+
/**
1886+
* Test available disk space for updates.
1887+
*
1888+
* @since 5.9.0
1889+
*
1890+
* @return array The test results.
1891+
*/
1892+
public function get_test_available_updates_disk_space() {
1893+
$available_space = function_exists( 'disk_free_space' ) ? (int) @disk_free_space( WP_CONTENT_DIR . '/upgrade/' ) : false;
1894+
$available_space_in_mb = $available_space / MB_IN_BYTES;
1895+
1896+
$result = array(
1897+
'label' => __( 'Disk space available to safely perform updates' ),
1898+
'status' => 'good',
1899+
'badge' => array(
1900+
'label' => __( 'Security' ),
1901+
'color' => 'blue',
1902+
),
1903+
'description' => sprintf(
1904+
/* translators: %s: Available disk space in MB or GB. */
1905+
'<p>' . __( '%s available disk space was detected, update routines can be performed safely.' ),
1906+
size_format( $available_space )
1907+
),
1908+
'actions' => '',
1909+
'test' => 'available_updates_disk_space',
1910+
);
1911+
1912+
if ( $available_space_in_mb < 100 ) {
1913+
$result['description'] = __( 'Available disk space is low, less than 100 MB available.' );
1914+
$result['status'] = 'recommended';
1915+
}
1916+
1917+
if ( $available_space_in_mb < 20 ) {
1918+
$result['description'] = __( 'Available disk space is critically low, less than 20 MB available. Proceed with caution, updates may fail.' );
1919+
$result['status'] = 'critical';
1920+
}
1921+
1922+
if ( ! $available_space ) {
1923+
$result['description'] = __( 'Could not determine available disk space for updates.' );
1924+
$result['status'] = 'recommended';
1925+
}
1926+
1927+
return $result;
1928+
}
1929+
1930+
/**
1931+
* Test if plugin and theme updates temp-backup directories are writable or can be created.
1932+
*
1933+
* @since 5.9.0
1934+
*
1935+
* @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
1936+
*
1937+
* @return array The test results.
1938+
*/
1939+
public function get_test_update_temp_backup_writable() {
1940+
global $wp_filesystem;
1941+
1942+
$result = array(
1943+
'label' => sprintf(
1944+
/* translators: %s: temp-backup */
1945+
__( 'Plugin and theme update %s directory is writable' ),
1946+
'temp-backup'
1947+
),
1948+
'status' => 'good',
1949+
'badge' => array(
1950+
'label' => __( 'Security' ),
1951+
'color' => 'blue',
1952+
),
1953+
'description' => sprintf(
1954+
/* translators: %s: wp-content/upgrade/temp-backup */
1955+
'<p>' . __( 'The %s directory used to improve the stability of plugin and theme updates is writable.' ),
1956+
'<code>wp-content/upgrade/temp-backup</code>'
1957+
),
1958+
'actions' => '',
1959+
'test' => 'update_temp_backup_writable',
1960+
);
1961+
1962+
if ( ! $wp_filesystem ) {
1963+
if ( ! function_exists( 'WP_Filesystem' ) ) {
1964+
require_once wp_normalize_path( ABSPATH . '/wp-admin/includes/file.php' );
1965+
}
1966+
WP_Filesystem();
1967+
}
1968+
$wp_content = $wp_filesystem->wp_content_dir();
1969+
1970+
$upgrade_dir_exists = $wp_filesystem->is_dir( "$wp_content/upgrade" );
1971+
$upgrade_dir_is_writable = $wp_filesystem->is_writable( "$wp_content/upgrade" );
1972+
$backup_dir_exists = $wp_filesystem->is_dir( "$wp_content/upgrade/temp-backup" );
1973+
$backup_dir_is_writable = $wp_filesystem->is_writable( "$wp_content/upgrade/temp-backup" );
1974+
1975+
$plugins_dir_exists = $wp_filesystem->is_dir( "$wp_content/upgrade/temp-backup/plugins" );
1976+
$plugins_dir_is_writable = $wp_filesystem->is_writable( "$wp_content/upgrade/temp-backup/plugins" );
1977+
$themes_dir_exists = $wp_filesystem->is_dir( "$wp_content/upgrade/temp-backup/themes" );
1978+
$themes_dir_is_writable = $wp_filesystem->is_writable( "$wp_content/upgrade/temp-backup/themes" );
1979+
1980+
if ( $plugins_dir_exists && ! $plugins_dir_is_writable && $themes_dir_exists && ! $themes_dir_is_writable ) {
1981+
$result['status'] = 'critical';
1982+
$result['label'] = sprintf(
1983+
/* translators: %s: temp-backup */
1984+
__( 'Plugins and themes %s directories exist but are not writable' ),
1985+
'temp-backup'
1986+
);
1987+
$result['description'] = sprintf(
1988+
/* translators: 1: wp-content/upgrade/temp-backup/plugins, 2: wp-content/upgrade/temp-backup/themes. */
1989+
'<p>' . __( 'The %1$s and %2$s directories exist but are not writable. These directories are used to improve the stability of plugin updates. Please make sure the server has write permissions to these directories.' ) . '</p>',
1990+
'<code>wp-content/upgrade/temp-backup/plugins</code>',
1991+
'<code>wp-content/upgrade/temp-backup/themes</code>'
1992+
);
1993+
return $result;
1994+
}
1995+
1996+
if ( $plugins_dir_exists && ! $plugins_dir_is_writable ) {
1997+
$result['status'] = 'critical';
1998+
$result['label'] = sprintf(
1999+
/* translators: %s: temp-backup */
2000+
__( 'Plugins %s directory exists but is not writable' ),
2001+
'temp-backup'
2002+
);
2003+
$result['description'] = sprintf(
2004+
/* translators: %s: wp-content/upgrade/temp-backup/plugins */
2005+
'<p>' . __( 'The %s directory exists but is not writable. This directory is used to improve the stability of plugin updates. Please make sure the server has write permissions to this directory.' ) . '</p>',
2006+
'<code>wp-content/upgrade/temp-backup/plugins</code>'
2007+
);
2008+
return $result;
2009+
}
2010+
2011+
if ( $themes_dir_exists && ! $themes_dir_is_writable ) {
2012+
$result['status'] = 'critical';
2013+
$result['label'] = sprintf(
2014+
/* translators: %s: temp-backup */
2015+
__( 'Themes %s directory exists but is not writable' ),
2016+
'temp-backup'
2017+
);
2018+
$result['description'] = sprintf(
2019+
/* translators: %s: wp-content/upgrade/temp-backup/themes */
2020+
'<p>' . __( 'The %s directory exists but is not writable. This directory is used to improve the stability of theme updates. Please make sure the server has write permissions to this directory.' ) . '</p>',
2021+
'<code>wp-content/upgrade/temp-backup/themes</code>'
2022+
);
2023+
return $result;
2024+
}
2025+
2026+
if ( ( ! $plugins_dir_exists || ! $themes_dir_exists ) && $backup_dir_exists && ! $backup_dir_is_writable ) {
2027+
$result['status'] = 'critical';
2028+
$result['label'] = sprintf(
2029+
/* translators: %s: temp-backup */
2030+
__( 'The %s directory exists but is not writable' ),
2031+
'temp-backup'
2032+
);
2033+
$result['description'] = sprintf(
2034+
/* translators: %s: wp-content/upgrade/temp-backup */
2035+
'<p>' . __( 'The %s directory exists but is not writable. This directory is used to improve the stability of plugin and theme updates. Please make sure the server has write permissions to this directory.' ) . '</p>',
2036+
'<code>wp-content/upgrade/temp-backup</code>'
2037+
);
2038+
return $result;
2039+
}
2040+
2041+
if ( ! $backup_dir_exists && $upgrade_dir_exists && ! $upgrade_dir_is_writable ) {
2042+
$result['status'] = 'critical';
2043+
$result['label'] = sprintf(
2044+
/* translators: %s: upgrade */
2045+
__( 'The %s directory exists but is not writable' ),
2046+
'upgrade'
2047+
);
2048+
$result['description'] = sprintf(
2049+
/* translators: %s: wp-content/upgrade */
2050+
'<p>' . __( 'The %s directory exists but is not writable. This directory is used for plugin and theme updates. Please make sure the server has write permissions to this directory.' ) . '</p>',
2051+
'<code>wp-content/upgrade</code>'
2052+
);
2053+
return $result;
2054+
}
2055+
2056+
if ( ! $upgrade_dir_exists && ! $wp_filesystem->is_writable( $wp_content ) ) {
2057+
$result['status'] = 'critical';
2058+
$result['label'] = sprintf(
2059+
/* translators: %s: upgrade */
2060+
__( 'The %s directory cannot be created' ),
2061+
'upgrade'
2062+
);
2063+
$result['description'] = sprintf(
2064+
/* translators: 1: wp-content/upgrade, 2: wp-content. */
2065+
'<p>' . __( 'The %1$s directory does not exist, and the server does not have write permissions in %2$s to create it. This directory is used for plugin and theme updates. Please make sure the server has write permissions in %2$s.' ) . '</p>',
2066+
'<code>wp-content/upgrade</code>',
2067+
'<code>wp-content</code>'
2068+
);
2069+
return $result;
2070+
}
2071+
2072+
return $result;
2073+
}
2074+
18852075
/**
18862076
* Test if loopbacks work as expected.
18872077
*
@@ -2266,71 +2456,80 @@ public function get_test_authorization_header() {
22662456
public static function get_tests() {
22672457
$tests = array(
22682458
'direct' => array(
2269-
'wordpress_version' => array(
2459+
'wordpress_version' => array(
22702460
'label' => __( 'WordPress Version' ),
22712461
'test' => 'wordpress_version',
22722462
),
2273-
'plugin_version' => array(
2463+
'plugin_version' => array(
22742464
'label' => __( 'Plugin Versions' ),
22752465
'test' => 'plugin_version',
22762466
),
2277-
'theme_version' => array(
2467+
'theme_version' => array(
22782468
'label' => __( 'Theme Versions' ),
22792469
'test' => 'theme_version',
22802470
),
2281-
'php_version' => array(
2471+
'php_version' => array(
22822472
'label' => __( 'PHP Version' ),
22832473
'test' => 'php_version',
22842474
),
2285-
'php_extensions' => array(
2475+
'php_extensions' => array(
22862476
'label' => __( 'PHP Extensions' ),
22872477
'test' => 'php_extensions',
22882478
),
2289-
'php_default_timezone' => array(
2479+
'php_default_timezone' => array(
22902480
'label' => __( 'PHP Default Timezone' ),
22912481
'test' => 'php_default_timezone',
22922482
),
2293-
'php_sessions' => array(
2483+
'php_sessions' => array(
22942484
'label' => __( 'PHP Sessions' ),
22952485
'test' => 'php_sessions',
22962486
),
2297-
'sql_server' => array(
2487+
'sql_server' => array(
22982488
'label' => __( 'Database Server version' ),
22992489
'test' => 'sql_server',
23002490
),
2301-
'utf8mb4_support' => array(
2491+
'utf8mb4_support' => array(
23022492
'label' => __( 'MySQL utf8mb4 support' ),
23032493
'test' => 'utf8mb4_support',
23042494
),
2305-
'ssl_support' => array(
2495+
'ssl_support' => array(
23062496
'label' => __( 'Secure communication' ),
23072497
'test' => 'ssl_support',
23082498
),
2309-
'scheduled_events' => array(
2499+
'scheduled_events' => array(
23102500
'label' => __( 'Scheduled events' ),
23112501
'test' => 'scheduled_events',
23122502
),
2313-
'http_requests' => array(
2503+
'http_requests' => array(
23142504
'label' => __( 'HTTP Requests' ),
23152505
'test' => 'http_requests',
23162506
),
2317-
'rest_availability' => array(
2507+
'rest_availability' => array(
23182508
'label' => __( 'REST API availability' ),
23192509
'test' => 'rest_availability',
23202510
'skip_cron' => true,
23212511
),
2322-
'debug_enabled' => array(
2512+
'debug_enabled' => array(
23232513
'label' => __( 'Debugging enabled' ),
23242514
'test' => 'is_in_debug_mode',
23252515
),
2326-
'file_uploads' => array(
2516+
'file_uploads' => array(
23272517
'label' => __( 'File uploads' ),
23282518
'test' => 'file_uploads',
23292519
),
2330-
'plugin_theme_auto_updates' => array(
2520+
'plugin_theme_auto_updates' => array(
23312521
'label' => __( 'Plugin and theme auto-updates' ),
23322522
'test' => 'plugin_theme_auto_updates',
23332523
),
2524+
'update_temp_backup_writable' => array(
2525+
/* translators: %s: temp-backup */
2526+
'label' => sprintf( __( 'Updates %s directory access' ), 'temp-backup' ),
2527+
'test' => 'update_temp_backup_writable',
2528+
),
2529+
'available_updates_disk_space' => array(
2530+
'label' => __( 'Available disk space' ),
2531+
'test' => 'available_updates_disk_space',
2532+
),
23342533
),
23352534
'async' => array(
23362535
'dotorg_communication' => array(

0 commit comments

Comments
 (0)