Skip to content

Commit 4ac1cb1

Browse files
authored
Merge branch 'main' into copilot/improve-db-update-commands
2 parents 994ae79 + c8fd1ed commit 4ac1cb1

File tree

7 files changed

+197
-12
lines changed

7 files changed

+197
-12
lines changed

.github/workflows/code-quality.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ on:
66
branches:
77
- main
88
- master
9+
schedule:
10+
- cron: '17 2 * * *' # Run every day on a seemly random time.
911

1012
jobs:
1113
code-quality:

.github/workflows/copilot-setup-steps.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717

1818
steps:
1919
- name: Checkout code
20-
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
20+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
2121

2222
- name: Check existence of composer.json file
2323
id: check_composer_file

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
],
1414
"require": {
1515
"composer/semver": "^1.4 || ^2 || ^3",
16-
"wp-cli/wp-cli": "^2.12"
16+
"wp-cli/wp-cli": "^2.13"
1717
},
1818
"require-dev": {
1919
"wp-cli/checksum-command": "^1 || ^2",

features/core-check-update.feature

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,19 @@ Feature: Check for more recent versions
7070
"""
7171
---
7272
"""
73+
74+
Scenario: Check update shows warning when version check API fails
75+
Given a WP install
76+
And that HTTP requests to https://api.wordpress.org/core/version-check/1.7/ will respond with:
77+
"""
78+
HTTP/1.1 500 Internal Server Error
79+
Content-Type: text/plain
80+
81+
<Error body>
82+
"""
83+
84+
When I try `wp core check-update --force-check`
85+
Then STDERR should contain:
86+
"""
87+
Warning: Failed to check for updates
88+
"""

features/core-update.feature

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ Feature: Update WordPress core
167167
When I run `wp core update`
168168
Then STDOUT should contain:
169169
"""
170-
WordPress is up to date
170+
WordPress is up to date at version
171171
"""
172172
And STDOUT should not contain:
173173
"""
@@ -420,6 +420,42 @@ Feature: Update WordPress core
420420
</div>
421421
"""
422422

423+
@require-php-7.0 @require-wp-6.1
424+
Scenario: Attempting to downgrade without --force shows helpful message
425+
Given a WP install
426+
427+
When I run `wp core version`
428+
Then save STDOUT as {WP_CURRENT_VERSION}
429+
430+
When I try `wp core update --version=6.0`
431+
Then STDOUT should contain:
432+
"""
433+
WordPress is up to date at version
434+
"""
435+
And STDOUT should contain:
436+
"""
437+
is older than the current version
438+
"""
439+
And STDOUT should contain:
440+
"""
441+
Use --force to update anyway
442+
"""
443+
And STDOUT should not contain:
444+
"""
445+
Success:
446+
"""
447+
And the return code should be 0
448+
449+
When I run `wp core update --version=6.0 --force`
450+
Then STDOUT should contain:
451+
"""
452+
Updating to version 6.0
453+
"""
454+
And STDOUT should contain:
455+
"""
456+
Success: WordPress updated successfully.
457+
"""
458+
423459
Scenario: Show helpful tip when update is locked
424460
Given a WP install
425461

@@ -436,4 +472,4 @@ Feature: Update WordPress core
436472
Then STDOUT should contain:
437473
"""
438474
Success:
439-
"""
475+
"""

src/Core_Command.php

Lines changed: 133 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,18 @@
2828
* 4.5.2
2929
*
3030
* @package wp-cli
31+
*
32+
* @phpstan-type HTTP_Response array{headers: array<string, string>, body: string, response: array{code:false|int, message: false|string}, cookies: array<string, string>, http_response: mixed}
3133
*/
3234
class Core_Command extends WP_CLI_Command {
3335

36+
/**
37+
* Stores HTTP API errors encountered during version check.
38+
*
39+
* @var \WP_Error|null
40+
*/
41+
private $version_check_error = null;
42+
3443
/**
3544
* Checks for WordPress updates via Version Check API.
3645
*
@@ -92,6 +101,14 @@ public function check_update( $args, $assoc_args ) {
92101
[ 'version', 'update_type', 'package_url' ]
93102
);
94103
$formatter->display_items( $updates );
104+
} elseif ( $this->version_check_error ) {
105+
// If there was an HTTP error during version check, show a warning
106+
WP_CLI::warning(
107+
sprintf(
108+
'Failed to check for updates: %s',
109+
$this->version_check_error->get_error_message()
110+
)
111+
);
95112
} else {
96113
WP_CLI::success( 'WordPress is at the latest version.' );
97114
}
@@ -644,6 +661,10 @@ private static function set_multisite_defaults( $assoc_args ) {
644661
}
645662

646663
private function do_install( $assoc_args ) {
664+
/**
665+
* @var \wpdb $wpdb
666+
*/
667+
global $wpdb;
647668
if ( is_blog_installed() ) {
648669
return false;
649670
}
@@ -695,7 +716,7 @@ function wp_new_blog_notification() {
695716
$args['locale']
696717
);
697718

698-
if ( ! empty( $GLOBALS['wpdb']->last_error ) ) {
719+
if ( ! empty( $wpdb->last_error ) ) {
699720
WP_CLI::error( 'Installation produced database errors, and may have partially or completely failed.' );
700721
}
701722

@@ -1283,8 +1304,14 @@ static function () {
12831304

12841305
WP_CLI::success( 'WordPress updated successfully.' );
12851306
}
1307+
// Check if user attempted to downgrade without --force
1308+
} elseif ( ! empty( Utils\get_flag_value( $assoc_args, 'version' ) ) && version_compare( Utils\get_flag_value( $assoc_args, 'version' ), $wp_version, '<' ) ) {
1309+
// Requested version is older than current (downgrade attempt)
1310+
WP_CLI::log( "WordPress is up to date at version {$wp_version}." );
1311+
WP_CLI::log( 'The version you requested (' . Utils\get_flag_value( $assoc_args, 'version' ) . ") is older than the current version ({$wp_version})." );
1312+
WP_CLI::log( 'Use --force to update anyway (e.g., to downgrade to version ' . Utils\get_flag_value( $assoc_args, 'version' ) . ').' );
12861313
} else {
1287-
WP_CLI::success( 'WordPress is up to date.' );
1314+
WP_CLI::success( "WordPress is up to date at version {$wp_version}." );
12881315
}
12891316
}
12901317

@@ -1555,7 +1582,25 @@ private function get_download_url( $version, $locale = 'en_US', $file_type = 'zi
15551582
*/
15561583
private function get_updates( $assoc_args ) {
15571584
$force_check = Utils\get_flag_value( $assoc_args, 'force-check' );
1558-
wp_version_check( [], $force_check );
1585+
1586+
// Reset error tracking
1587+
$this->version_check_error = null;
1588+
1589+
// Hook into pre_http_request with max priority to capture errors during version check
1590+
// This is necessary because tests and plugins may use pre_http_request to mock responses
1591+
add_filter( 'pre_http_request', [ $this, 'capture_version_check_error' ], PHP_INT_MAX, 3 );
1592+
1593+
// Also hook into http_api_debug to capture errors from real HTTP requests
1594+
// This fires when pre_http_request doesn't short-circuit the request
1595+
add_action( 'http_api_debug', [ $this, 'capture_version_check_error_from_response' ], 10, 5 );
1596+
1597+
try {
1598+
wp_version_check( [], $force_check );
1599+
} finally {
1600+
// Ensure the hooks are always removed, even if wp_version_check() throws an exception
1601+
remove_filter( 'pre_http_request', [ $this, 'capture_version_check_error' ], PHP_INT_MAX );
1602+
remove_action( 'http_api_debug', [ $this, 'capture_version_check_error_from_response' ], 10 );
1603+
}
15591604

15601605
/**
15611606
* @var object{updates: array<object{version: string, locale: string, packages: object{partial?: string, full: string}}>}|false $from_api
@@ -1565,6 +1610,10 @@ private function get_updates( $assoc_args ) {
15651610
return [];
15661611
}
15671612

1613+
/**
1614+
* @var array{wp_version: string} $GLOBALS
1615+
*/
1616+
15681617
$compare_version = str_replace( '-src', '', $GLOBALS['wp_version'] );
15691618

15701619
$updates = [
@@ -1612,6 +1661,87 @@ private function get_updates( $assoc_args ) {
16121661
return array_values( $updates );
16131662
}
16141663

1664+
/**
1665+
* Sets or clears the version check error property based on an HTTP response.
1666+
*
1667+
* @param mixed|\WP_Error $response The HTTP response (WP_Error, array, or other).
1668+
*
1669+
* @phpstan-param HTTP_Response|WP_Error $response
1670+
*/
1671+
private function set_version_check_error( $response ) {
1672+
if ( is_wp_error( $response ) ) {
1673+
$this->version_check_error = $response;
1674+
} elseif ( is_array( $response ) && isset( $response['response']['code'] ) && $response['response']['code'] >= 400 ) {
1675+
// HTTP error status code (4xx or 5xx) - convert to WP_Error for consistency
1676+
$this->version_check_error = new \WP_Error(
1677+
'http_request_failed',
1678+
sprintf(
1679+
'HTTP request failed with status %d: %s',
1680+
$response['response']['code'],
1681+
isset( $response['response']['message'] ) ? $response['response']['message'] : 'Unknown error'
1682+
)
1683+
);
1684+
} else {
1685+
// Clear any previous error if we got a successful response.
1686+
$this->version_check_error = null;
1687+
}
1688+
}
1689+
1690+
/**
1691+
* Handles the pre_http_request filter to capture HTTP errors during version check.
1692+
*
1693+
* This method signature matches the pre_http_request filter signature.
1694+
* Uses PHP_INT_MAX priority to run after test mocking and plugin modifications.
1695+
*
1696+
* @param false|array|WP_Error $response A preemptive return value of an HTTP request.
1697+
* @param array $args HTTP request arguments.
1698+
* @param string $url The request URL.
1699+
* @return false|array|WP_Error The response, unmodified.
1700+
*
1701+
* @phpstan-param HTTP_Response|WP_Error|false $response
1702+
*/
1703+
public function capture_version_check_error( $response, $args, $url ) {
1704+
if ( false === strpos( $url, 'api.wordpress.org/core/version-check' ) ) {
1705+
return $response;
1706+
}
1707+
1708+
// A `false` response means the request is not being preempted.
1709+
// In this case, we don't want to change the error status, as the subsequent
1710+
// `http_api_debug` hook will handle the actual response.
1711+
if ( false !== $response ) {
1712+
$this->set_version_check_error( $response );
1713+
}
1714+
1715+
return $response;
1716+
}
1717+
1718+
/**
1719+
* Handles the http_api_debug action to capture HTTP errors from real requests.
1720+
*
1721+
* This fires when pre_http_request doesn't short-circuit the request.
1722+
* Uses the http_api_debug action hook signature.
1723+
*
1724+
* @param array|WP_Error $response HTTP response or WP_Error object.
1725+
* @param string $context Context of the HTTP request.
1726+
* @param string $_class HTTP transport class name (unused).
1727+
* @param array $_args HTTP request arguments (unused).
1728+
* @param string $url URL being requested.
1729+
*
1730+
* @phpstan-param HTTP_Response|WP_Error $response
1731+
*/
1732+
public function capture_version_check_error_from_response( $response, $context, $_class, $_args, $url ) {
1733+
if ( false === strpos( $url, 'api.wordpress.org/core/version-check' ) ) {
1734+
return;
1735+
}
1736+
1737+
// Only capture on response, not pre_response
1738+
if ( 'response' !== $context ) {
1739+
return;
1740+
}
1741+
1742+
$this->set_version_check_error( $response );
1743+
}
1744+
16151745
/**
16161746
* Clean up extra files.
16171747
*

src/WP_CLI/Core/CoreUpgrader.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,8 @@ public function download_package( $package, $check_signatures = false, $hook_ext
6767
$hook_extra
6868
);
6969

70-
/**
71-
* @var false|string|\WP_Error $reply
72-
*/
73-
7470
if ( false !== $reply ) {
71+
// @phpstan-ignore return.type
7572
return $reply;
7673
}
7774

@@ -96,7 +93,11 @@ function () use ( $temp ) {
9693
}
9794
);
9895

99-
$cache = WP_CLI::get_cache();
96+
$cache = WP_CLI::get_cache();
97+
98+
/**
99+
* @var object{locale: string} $update
100+
*/
100101
$update = $GLOBALS['wpcli_core_update_obj'];
101102
$cache_key = "core/{$filename}-{$update->locale}.{$extension}";
102103

0 commit comments

Comments
 (0)