Make WordPress Core


Ignore:
Timestamp:
05/10/2024 11:46:51 AM (5 months ago)
Author:
johnbillion
Message:

Upgrade/Install: Automatically roll back to the previous version when an automatic plugin update results in a fatal error on the front end of the site.

This builds on the temporary backup system introduced in 6.3 to allow automatic updates to benefit from fatal error protection. A loopback request is performed to the home page of the site and the plugin is rolled back to its backed up version if a fatal error is observed.

For debugging and observability during beta, this change includes several calls to error_log() during the upgrade and rollback stages. These calls can be removed or placed behind a flag once we're ready for RC1.

Props costdev, johnbillion, mukesh27, afragen, audrasjb, justlevine, kirasong, peterwilsoncc

Fixes #58281

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/includes/class-wp-upgrader.php

    r58105 r58128  
    902902
    903903        if ( is_wp_error( $result ) ) {
     904            // An automatic plugin update will have already performed its rollback.
    904905            if ( ! empty( $options['hook_extra']['temp_backup'] ) ) {
    905906                $this->temp_restores[] = $options['hook_extra']['temp_backup'];
     
    910911                 * so in case the failure was due to a PHP timeout,
    911912                 * it will still be able to properly restore the previous version.
     913                 *
     914                 * Zero arguments are accepted as a string can sometimes be passed
     915                 * internally during actions, causing an error because
     916                 * `WP_Upgrader::restore_temp_backup()` expects an array.
    912917                 */
    913                 add_action( 'shutdown', array( $this, 'restore_temp_backup' ) );
     918                add_action( 'shutdown', array( $this, 'restore_temp_backup' ), 10, 0 );
    914919            }
    915920            $this->skin->error( $result );
     
    984989    public function maintenance_mode( $enable = false ) {
    985990        global $wp_filesystem;
     991
     992        if ( ! $wp_filesystem ) {
     993            require_once ABSPATH . 'wp-admin/includes/file.php';
     994            WP_Filesystem();
     995        }
     996
    986997        $file = $wp_filesystem->abspath() . '.maintenance';
    987998        if ( $enable ) {
    988             $this->skin->feedback( 'maintenance_start' );
     999            if ( ! wp_doing_cron() ) {
     1000                $this->skin->feedback( 'maintenance_start' );
     1001            }
    9891002            // Create maintenance file to signal that we are upgrading.
    9901003            $maintenance_string = '<?php $upgrading = ' . time() . '; ?>';
     
    9921005            $wp_filesystem->put_contents( $file, $maintenance_string, FS_CHMOD_FILE );
    9931006        } elseif ( ! $enable && $wp_filesystem->exists( $file ) ) {
    994             $this->skin->feedback( 'maintenance_end' );
     1007            if ( ! wp_doing_cron() ) {
     1008                $this->skin->feedback( 'maintenance_end' );
     1009            }
    9951010            $wp_filesystem->delete( $file );
    9961011        }
     
    11341149     *
    11351150     * @since 6.3.0
     1151     * @since 6.6.0 Added the `$temp_backups` parameter.
    11361152     *
    11371153     * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
    11381154     *
     1155     * @param array[] $temp_backups {
     1156     *     Optional. An array of temporary backups.
     1157     *
     1158     *     @type array ...$0 {
     1159     *         Information about the backup.
     1160     *
     1161     *         @type string $dir  The temporary backup location in the upgrade-temp-backup directory.
     1162     *         @type string $slug The item's slug.
     1163     *         @type string $src  The directory where the original is stored. For example, `WP_PLUGIN_DIR`.
     1164     *     }
     1165     * }
    11391166     * @return bool|WP_Error True on success, false on early exit, otherwise WP_Error.
    11401167     */
    1141     public function restore_temp_backup() {
     1168    public function restore_temp_backup( array $temp_backups = array() ) {
    11421169        global $wp_filesystem;
    11431170
    11441171        $errors = new WP_Error();
    11451172
    1146         foreach ( $this->temp_restores as $args ) {
     1173        if ( empty( $temp_backups ) ) {
     1174            $temp_backups = $this->temp_restores;
     1175        }
     1176
     1177        foreach ( $temp_backups as $args ) {
    11471178            if ( empty( $args['slug'] ) || empty( $args['src'] ) || empty( $args['dir'] ) ) {
    11481179                return false;
     
    11871218     *
    11881219     * @since 6.3.0
     1220     * @since 6.6.0 Added the `$temp_backups` parameter.
    11891221     *
    11901222     * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
    11911223     *
     1224     * @param array[] $temp_backups {
     1225     *     Optional. An array of temporary backups.
     1226     *
     1227     *     @type array ...$0 {
     1228     *         Information about the backup.
     1229     *
     1230     *         @type string $dir  The temporary backup location in the upgrade-temp-backup directory.
     1231     *         @type string $slug The item's slug.
     1232     *         @type string $src  The directory where the original is stored. For example, `WP_PLUGIN_DIR`.
     1233     *     }
     1234     * }
    11921235     * @return bool|WP_Error True on success, false on early exit, otherwise WP_Error.
    11931236     */
    1194     public function delete_temp_backup() {
     1237    public function delete_temp_backup( array $temp_backups = array() ) {
    11951238        global $wp_filesystem;
    11961239
    11971240        $errors = new WP_Error();
    11981241
    1199         foreach ( $this->temp_backups as $args ) {
     1242        if ( empty( $temp_backups ) ) {
     1243            $temp_backups = $this->temp_backups;
     1244        }
     1245
     1246        foreach ( $temp_backups as $args ) {
    12001247            if ( empty( $args['slug'] ) || empty( $args['dir'] ) ) {
    12011248                return false;
Note: See TracChangeset for help on using the changeset viewer.