diff --git a/features/core-update.feature b/features/core-update.feature index 471e4096..6576f089 100644 --- a/features/core-update.feature +++ b/features/core-update.feature @@ -202,7 +202,8 @@ Feature: Update WordPress core Success: WordPress updated successfully. """ - When I run `wp core verify-checksums` + # Allow for warnings to be produced. + When I try `wp core verify-checksums` Then STDOUT should be: """ Success: WordPress installation verifies against checksums. @@ -250,6 +251,81 @@ Feature: Update WordPress core When I run `wp post create --post_title='Test post' --porcelain` Then STDOUT should be a number + Scenario: Make sure files are cleaned up with mixed case + Given a WP install + And I try `wp theme install twentytwenty --activate` + + When I run `wp core update --version=5.8 --force` + Then the wp-includes/Requests/Transport/cURL.php file should exist + Then the wp-includes/Requests/Exception/Transport/cURL.php file should exist + Then the wp-includes/Requests/Exception/HTTP/502.php file should exist + Then the wp-includes/Requests/IRI.php file should exist + Then the wp-includes/Requests/Transport/Curl.php file should not exist + Then the wp-includes/Requests/Exception/Transport/Curl.php file should not exist + Then the wp-includes/Requests/Exception/Http/Status502.php file should not exist + Then the wp-includes/Requests/Iri.php file should not exist + + When I run `wp core update --version=5.9-beta1 --force` + Then the wp-includes/Requests/Transport/cURL.php file should not exist + Then the wp-includes/Requests/Exception/Transport/cURL.php file should not exist + Then the wp-includes/Requests/Exception/HTTP/502.php file should not exist + Then the wp-includes/Requests/IRI.php file should not exist + Then the wp-includes/Requests/Transport/Curl.php file should exist + Then the wp-includes/Requests/Exception/Transport/Curl.php file should exist + Then the wp-includes/Requests/Exception/Http/Status502.php file should exist + Then the wp-includes/Requests/Iri.php file should exist + Then STDOUT should contain: + """ + File removed: wp-includes/Requests/Transport/fsockopen.php + File removed: wp-includes/Requests/Transport/cURL.php + File removed: wp-includes/Requests/Hooker.php + File removed: wp-includes/Requests/IPv6.php + File removed: wp-includes/Requests/Exception/Transport/cURL.php + File removed: wp-includes/Requests/Exception/HTTP.php + File removed: wp-includes/Requests/Exception/HTTP/502.php + File removed: wp-includes/Requests/Exception/HTTP/Unknown.php + File removed: wp-includes/Requests/Exception/HTTP/412.php + File removed: wp-includes/Requests/Exception/HTTP/408.php + File removed: wp-includes/Requests/Exception/HTTP/431.php + File removed: wp-includes/Requests/Exception/HTTP/501.php + File removed: wp-includes/Requests/Exception/HTTP/500.php + File removed: wp-includes/Requests/Exception/HTTP/407.php + File removed: wp-includes/Requests/Exception/HTTP/416.php + File removed: wp-includes/Requests/Exception/HTTP/428.php + File removed: wp-includes/Requests/Exception/HTTP/406.php + File removed: wp-includes/Requests/Exception/HTTP/504.php + File removed: wp-includes/Requests/Exception/HTTP/411.php + File removed: wp-includes/Requests/Exception/HTTP/414.php + File removed: wp-includes/Requests/Exception/HTTP/511.php + File removed: wp-includes/Requests/Exception/HTTP/410.php + File removed: wp-includes/Requests/Exception/HTTP/403.php + File removed: wp-includes/Requests/Exception/HTTP/400.php + File removed: wp-includes/Requests/Exception/HTTP/505.php + File removed: wp-includes/Requests/Exception/HTTP/413.php + File removed: wp-includes/Requests/Exception/HTTP/404.php + File removed: wp-includes/Requests/Exception/HTTP/306.php + File removed: wp-includes/Requests/Exception/HTTP/304.php + File removed: wp-includes/Requests/Exception/HTTP/405.php + File removed: wp-includes/Requests/Exception/HTTP/429.php + File removed: wp-includes/Requests/Exception/HTTP/417.php + File removed: wp-includes/Requests/Exception/HTTP/409.php + File removed: wp-includes/Requests/Exception/HTTP/402.php + File removed: wp-includes/Requests/Exception/HTTP/418.php + File removed: wp-includes/Requests/Exception/HTTP/305.php + File removed: wp-includes/Requests/Exception/HTTP/415.php + File removed: wp-includes/Requests/Exception/HTTP/401.php + File removed: wp-includes/Requests/Exception/HTTP/503.php + File removed: wp-includes/Requests/IRI.php + File removed: wp-includes/Requests/IDNAEncoder.php + File removed: wp-includes/Requests/SSL.php + File removed: wp-includes/Requests/Proxy/HTTP.php + """ + + When I run `wp option add str_opt 'bar'` + Then STDOUT should not be empty + When I run `wp post create --post_title='Test post' --porcelain` + Then STDOUT should be a number + @less-than-php-7.3 Scenario: Minor update on an unlocalized WordPress release Given a WP install diff --git a/src/Core_Command.php b/src/Core_Command.php index 2b33592d..8ff2c733 100644 --- a/src/Core_Command.php +++ b/src/Core_Command.php @@ -1372,7 +1372,69 @@ private function cleanup_extra_files( $version_from, $version_to, $locale, $inse return; } - $files_to_remove = array_diff( array_keys( $old_checksums ), array_keys( $new_checksums ) ); + // Compare the files from the old version and the new version in a case-insensitive manner, + // to prevent files being incorrectly deleted on systems with case-insensitive filesystems + // when core changes the case of filenames. + // The main logic for this was taken from the Joomla project and adapted for WP. + // See: https://github.com/joomla/joomla-cms/blob/bb5368c7ef9c20270e6e9fcc4b364cd0849082a5/administrator/components/com_admin/script.php#L8158 + + $old_filepaths = array_keys( $old_checksums ); + $new_filepaths = array_keys( $new_checksums ); + + $new_filepaths = array_combine( array_map( 'strtolower', $new_filepaths ), $new_filepaths ); + + $old_filepaths_to_check = array_diff( $old_filepaths, $new_filepaths ); + + foreach ( $old_filepaths_to_check as $old_filepath_to_check ) { + $old_realpath = realpath( ABSPATH . $old_filepath_to_check ); + + // On Unix without incorrectly cased file. + if ( false === $old_realpath ) { + continue; + } + + $lowercase_old_filepath_to_check = strtolower( $old_filepath_to_check ); + + if ( ! array_key_exists( $lowercase_old_filepath_to_check, $new_filepaths ) ) { + $files_to_remove[] = $old_filepath_to_check; + continue; + } + + // We are now left with only the files that are similar from old to new except for their case. + + $old_basename = basename( $old_realpath ); + $new_filepath = $new_filepaths[ $lowercase_old_filepath_to_check ]; + $expected_basename = basename( $new_filepath ); + $new_realpath = realpath( ABSPATH . $new_filepath ); + $new_basename = basename( $new_realpath ); + + // On Windows or Unix with only the incorrectly cased file. + if ( $new_basename !== $expected_basename ) { + WP_CLI::debug( "Renaming file '{$old_filepath_to_check}' => '{$new_filepath}'", 'core' ); + + rename( ABSPATH . $old_filepath_to_check, ABSPATH . $old_filepath_to_check . '.tmp' ); + rename( ABSPATH . $old_filepath_to_check . '.tmp', ABSPATH . $new_filepath ); + + continue; + } + + // There might still be an incorrectly cased file on other OS than Windows. + if ( basename( $old_filepath_to_check ) === $old_basename ) { + // Check if case-insensitive file system, eg on OSX. + if ( fileinode( $old_realpath ) === fileinode( $new_realpath ) ) { + // Check deeper because even realpath or glob might not return the actual case. + if ( ! in_array( $expected_basename, scandir( dirname( $new_realpath ) ), true ) ) { + WP_CLI::debug( "Renaming file '{$old_filepath_to_check}' => '{$new_filepath}'", 'core' ); + + rename( ABSPATH . $old_filepath_to_check, ABSPATH . $old_filepath_to_check . '.tmp' ); + rename( ABSPATH . $old_filepath_to_check . '.tmp', ABSPATH . $new_filepath ); + } + } else { + // On Unix with both files: Delete the incorrectly cased file. + $files_to_remove[] = $old_filepath_to_check; + } + } + } if ( ! empty( $files_to_remove ) ) { WP_CLI::log( 'Cleaning up files...' );