Skip to content

Commit ed1adba

Browse files
committed
Merge branch 'main' into feature/wp-version-resolve
2 parents 9fd76dd + 5b42c27 commit ed1adba

20 files changed

+1169
-71
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ phpunit.xml
1212
phpcs.xml
1313
.phpcs.xml
1414
.phpunit.result.cache
15+
.phpunit.cache
1516
build/logs

AGENTS.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# Instructions
2+
3+
This package is part of WP-CLI, the official command line interface for WordPress. For a detailed explanation of the project structure and development workflow, please refer to the main @README.md file.
4+
5+
## Best Practices for Code Contributions
6+
7+
When contributing to this package, please adhere to the following guidelines:
8+
9+
* **Follow Existing Conventions:** Before writing any code, analyze the existing codebase in this package to understand the coding style, naming conventions, and architectural patterns.
10+
* **Focus on the Package's Scope:** All changes should be relevant to the functionality of the package.
11+
* **Write Tests:** All new features and bug fixes must be accompanied by acceptance tests using Behat. You can find the existing tests in the `features/` directory. There may be PHPUnit unit tests as well in the `tests/` directory.
12+
* **Update Documentation:** If your changes affect the user-facing functionality, please update the relevant inline code documentation.
13+
14+
### Building and running
15+
16+
Before submitting any changes, it is crucial to validate them by running the full suite of static code analysis and tests. To run the full suite of checks, execute the following command: `composer test`.
17+
18+
This single command ensures that your changes meet all the quality gates of the project. While you can run the individual steps separately, it is highly recommended to use this single command to ensure a comprehensive validation.
19+
20+
### Useful Composer Commands
21+
22+
The project uses Composer to manage dependencies and run scripts. The following commands are available:
23+
24+
* `composer install`: Install dependencies.
25+
* `composer test`: Run the full test suite, including linting, code style checks, static analysis, and unit/behavior tests.
26+
* `composer lint`: Check for syntax errors.
27+
* `composer phpcs`: Check for code style violations.
28+
* `composer phpcbf`: Automatically fix code style violations.
29+
* `composer phpstan`: Run static analysis.
30+
* `composer phpunit`: Run unit tests.
31+
* `composer behat`: Run behavior-driven tests.
32+
33+
### Coding Style
34+
35+
The project follows the `WP_CLI_CS` coding standard, which is enforced by PHP_CodeSniffer. The configuration can be found in `phpcs.xml.dist`. Before submitting any code, please run `composer phpcs` to check for violations and `composer phpcbf` to automatically fix them.
36+
37+
## Documentation
38+
39+
The `README.md` file might be generated dynamically from the project's codebase using `wp scaffold package-readme` ([doc](https://github.com/wp-cli/scaffold-package-command#wp-scaffold-package-readme)). In that case, changes need to be made against the corresponding part of the codebase.
40+
41+
### Inline Documentation
42+
43+
Only write high-value comments if at all. Avoid talking to the user through comments.
44+
45+
## Testing
46+
47+
The project has a comprehensive test suite that includes unit tests, behavior-driven tests, and static analysis.
48+
49+
* **Unit tests** are written with PHPUnit and can be found in the `tests/` directory. The configuration is in `phpunit.xml.dist`.
50+
* **Behavior-driven tests** are written with Behat and can be found in the `features/` directory. The configuration is in `behat.yml`.
51+
* **Static analysis** is performed with PHPStan.
52+
53+
All tests are run on GitHub Actions for every pull request.
54+
55+
When writing tests, aim to follow existing patterns. Key conventions include:
56+
57+
* When adding tests, first examine existing tests to understand and conform to established conventions.
58+
* For unit tests, extend the base `WP_CLI\Tests\TestCase` test class.
59+
* For Behat tests, only WP-CLI commands installed in `composer.json` can be run.
60+
61+
### Behat Steps
62+
63+
WP-CLI makes use of a Behat-based testing framework and provides a set of custom step definitions to write feature tests.
64+
65+
> **Note:** If you are expecting an error output in a test, you need to use `When I try ...` instead of `When I run ...` .
66+
67+
#### Given
68+
69+
* `Given an empty directory` - Creates an empty directory.
70+
* `Given /^an? (empty|non-existent) ([^\s]+) directory$/` - Creates or deletes a specific directory.
71+
* `Given an empty cache` - Clears the WP-CLI cache directory.
72+
* `Given /^an? ([^\s]+) (file|cache file):$/` - Creates a file with the given contents.
73+
* `Given /^"([^"]+)" replaced with "([^"]+)" in the ([^\s]+) file$/` - Search and replace a string in a file using regex.
74+
* `Given /^that HTTP requests to (.*?) will respond with:$/` - Mock HTTP requests to a given URL.
75+
* `Given WP files` - Download WordPress files without installing.
76+
* `Given wp-config.php` - Create a wp-config.php file using `wp config create`.
77+
* `Given a database` - Creates an empty database.
78+
* `Given a WP install(ation)` - Installs WordPress.
79+
* `Given a WP install(ation) in :subdir` - Installs WordPress in a given directory.
80+
* `Given a WP install(ation) with Composer` - Installs WordPress with Composer.
81+
* `Given a WP install(ation) with Composer and a custom vendor directory :vendor_directory` - Installs WordPress with Composer and a custom vendor directory.
82+
* `Given /^a WP multisite (subdirectory|subdomain)?\s?(install|installation)$/` - Installs WordPress Multisite.
83+
* `Given these installed and active plugins:` - Installs and activates one or more plugins.
84+
* `Given a custom wp-content directory` - Configure a custom `wp-content` directory.
85+
* `Given download:` - Download multiple files into the given destinations.
86+
* `Given /^save (STDOUT|STDERR) ([\'].+[^\'])?\s?as \{(\w+)\}$/` - Store STDOUT or STDERR contents in a variable.
87+
* `Given /^a new Phar with (?:the same version|version "([^"]+)")$/` - Build a new WP-CLI Phar file with a given version.
88+
* `Given /^a downloaded Phar with (?:the same version|version "([^"]+)")$/` - Download a specific WP-CLI Phar version from GitHub.
89+
* `Given /^save the (.+) file ([\'].+[^\'])? as \{(\w+)\}$/` - Stores the contents of the given file in a variable.
90+
* `Given a misconfigured WP_CONTENT_DIR constant directory` - Modify wp-config.php to set `WP_CONTENT_DIR` to an empty string.
91+
* `Given a dependency on current wp-cli` - Add `wp-cli/wp-cli` as a Composer dependency.
92+
* `Given a PHP built-in web server` - Start a PHP built-in web server in the current directory.
93+
* `Given a PHP built-in web server to serve :subdir` - Start a PHP built-in web server in the given subdirectory.
94+
95+
#### When
96+
97+
* ``When /^I launch in the background `([^`]+)`$/`` - Launch a given command in the background.
98+
* ``When /^I (run|try) `([^`]+)`$/`` - Run or try a given command.
99+
* ``When /^I (run|try) `([^`]+)` from '([^\s]+)'$/`` - Run or try a given command in a subdirectory.
100+
* `When /^I (run|try) the previous command again$/` - Run or try the previous command again.
101+
102+
#### Then
103+
104+
* `Then /^the return code should( not)? be (\d+)$/` - Expect a specific exit code of the previous command.
105+
* `Then /^(STDOUT|STDERR) should( strictly)? (be|contain|not contain):$/` - Check the contents of STDOUT or STDERR.
106+
* `Then /^(STDOUT|STDERR) should be a number$/` - Expect STDOUT or STDERR to be a numeric value.
107+
* `Then /^(STDOUT|STDERR) should not be a number$/` - Expect STDOUT or STDERR to not be a numeric value.
108+
* `Then /^STDOUT should be a table containing rows:$/` - Expect STDOUT to be a table containing the given rows.
109+
* `Then /^STDOUT should end with a table containing rows:$/` - Expect STDOUT to end with a table containing the given rows.
110+
* `Then /^STDOUT should be JSON containing:$/` - Expect valid JSON output in STDOUT.
111+
* `Then /^STDOUT should be a JSON array containing:$/` - Expect valid JSON array output in STDOUT.
112+
* `Then /^STDOUT should be CSV containing:$/` - Expect STDOUT to be CSV containing certain values.
113+
* `Then /^STDOUT should be YAML containing:$/` - Expect STDOUT to be YAML containing certain content.
114+
* `Then /^(STDOUT|STDERR) should be empty$/` - Expect STDOUT or STDERR to be empty.
115+
* `Then /^(STDOUT|STDERR) should not be empty$/` - Expect STDOUT or STDERR not to be empty.
116+
* `Then /^(STDOUT|STDERR) should be a version string (<|<=|>|>=|==|=|<>) ([+\w.{}-]+)$/` - Expect STDOUT or STDERR to be a version string comparing to the given version.
117+
* `Then /^the (.+) (file|directory) should( strictly)? (exist|not exist|be:|contain:|not contain):$/` - Expect a certain file or directory to (not) exist or (not) contain certain contents.
118+
* `Then /^the contents of the (.+) file should( not)? match (((\/.*\/)|(#.#))([a-z]+)?)$/` - Match file contents against a regex.
119+
* `Then /^(STDOUT|STDERR) should( not)? match (((\/.*\/)|(#.#))([a-z]+)?)$/` - Match STDOUT or STDERR against a regex.
120+
* `Then /^an email should (be sent|not be sent)$/` - Expect an email to be sent (or not).
121+
* `Then the HTTP status code should be :code` - Expect the HTTP status code for visiting `http://localhost:8080`.

bin/install-package-tests

Lines changed: 67 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,13 @@
1212
is_numeric() {
1313
case $1 in
1414
''|*[!0-9]*) return 1;; # returns 1 if not numeric
15-
*) return 0;; # returns 0 if numeric
15+
*) return 0;; # returns 0 if numeric
1616
esac
1717
}
18+
# Promt color vars.
19+
C_RED="\033[31m"
20+
C_BLUE="\033[34m"
21+
NO_FORMAT="\033[0m"
1822

1923
HOST=localhost
2024
PORT=""
@@ -28,36 +32,57 @@ if [ -n "${WP_CLI_TEST_DBHOST}" ]; then
2832
if [ -n "${PORT}" ]; then
2933
# If the port is not numeric, then we assume it is a socket path.
3034
if is_numeric "${PORT}"; then
35+
echo "Connecting to custom host: ${C_BLUE}${HOST}${NO_FORMAT} on port ${C_BLUE}${PORT}${NO_FORMAT}"
3136
HOST_STRING="${HOST_STRING} --port=${PORT} --protocol=tcp"
3237
else
38+
echo "Connecting to custom host: ${C_BLUE}${HOST}${NO_FORMAT} on socket ${C_BLUE}${PORT}${NO_FORMAT}"
3339
HOST_STRING="${HOST_STRING} --socket=${PORT} --protocol=socket"
3440
fi
41+
else
42+
echo "Connecting to custom host: ${C_BLUE}${HOST}${NO_FORMAT}"
3543
fi
44+
else
45+
echo "Connecting to default host: ${C_BLUE}${HOST}${NO_FORMAT}"
3646
fi
3747

3848
USER=root
3949
if [ -n "${WP_CLI_TEST_DBROOTUSER}" ]; then
40-
USER="${WP_CLI_TEST_DBROOTUSER}"
50+
echo "Connecting with custom root user: ${C_BLUE}${WP_CLI_TEST_DBROOTUSER}${NO_FORMAT}"
51+
USER="${WP_CLI_TEST_DBROOTUSER}"
52+
else
53+
echo "Connecting with default root user: ${C_BLUE}${USER}${NO_FORMAT}"
4154
fi
4255

4356
PASSWORD_STRING=""
4457
if [ -n "${WP_CLI_TEST_DBROOTPASS}" ]; then
45-
PASSWORD_STRING="-p${WP_CLI_TEST_DBROOTPASS}"
58+
echo "Connecting with custom root password: ${C_BLUE}${WP_CLI_TEST_DBROOTPASS}${NO_FORMAT}"
59+
PASSWORD_STRING="-p${WP_CLI_TEST_DBROOTPASS}"
60+
else
61+
echo "Connecting with default root password: ${C_BLUE}empty${NO_FORMAT}"
4662
fi
4763

4864
TEST_DB=wp_cli_test
4965
if [ -n "${WP_CLI_TEST_DBNAME}" ]; then
50-
TEST_DB="${WP_CLI_TEST_DBNAME}"
66+
echo "Using custom test database: ${C_BLUE}${WP_CLI_TEST_DBNAME}${NO_FORMAT}"
67+
TEST_DB="${WP_CLI_TEST_DBNAME}"
68+
else
69+
echo "Using default test database: ${C_BLUE}${TEST_DB}${NO_FORMAT}"
5170
fi
5271

5372
TEST_USER=wp_cli_test
5473
if [ -n "${WP_CLI_TEST_DBUSER}" ]; then
55-
TEST_USER="${WP_CLI_TEST_DBUSER}"
74+
echo "Using custom test user: ${C_BLUE}${WP_CLI_TEST_DBUSER}${NO_FORMAT}"
75+
TEST_USER="${WP_CLI_TEST_DBUSER}"
76+
else
77+
echo "Using default test user: ${C_BLUE}${TEST_USER}${NO_FORMAT}"
5678
fi
5779

5880
TEST_PASSWORD=password1
5981
if [ -n "${WP_CLI_TEST_DBPASS}" ]; then
60-
TEST_PASSWORD="${WP_CLI_TEST_DBPASS}"
82+
echo "Using custom test password: ${C_BLUE}${WP_CLI_TEST_DBPASS}${NO_FORMAT}"
83+
TEST_PASSWORD="${WP_CLI_TEST_DBPASS}"
84+
else
85+
echo "Using default test password: ${C_BLUE}${TEST_PASSWORD}${NO_FORMAT}"
6186
fi
6287

6388
echo "Detecting database version..."
@@ -71,6 +96,15 @@ case "${CLIENT_VERSION}" in
7196
;;
7297
esac
7398

99+
if [ -z "$PS1" ]; then
100+
# These vars are because github actions gave problems in the past.
101+
MYSQL_TRIES=36
102+
MYSQL_WAIT=5
103+
else
104+
MYSQL_TRIES=1
105+
MYSQL_WAIT=0
106+
fi
107+
74108
if [ "${TYPE}" = "MySQL" ]; then
75109
SERVER_VERSION=$(mysql -e "SELECT VERSION()" --skip-column-names ${HOST_STRING} -u"${USER}" "${PASSWORD_STRING}")
76110
else
@@ -88,42 +122,54 @@ echo 'Checking if database is ready...'
88122
if [ "${TYPE}" = "MySQL" ]; then
89123
while ! mysql ${HOST_STRING} --user="${USER}" "${PASSWORD_STRING}" --execute="SHOW DATABASES;" | grep 'information_schema' >/dev/null;
90124
do
91-
echo 'Waiting for database...'
92-
sleep 5
93125
i=$((i+1))
94-
if [ $i -gt 36 ]; then
95-
echo 'Database failed to start. Aborting.'
96-
exit 1
126+
if [ "${MYSQL_TRIES}" -gt 1 ]; then
127+
echo "Waiting for MySQL(${i}/${MYSQL_TRIES})..."
128+
sleep ${MYSQL_WAIT}
129+
fi
130+
131+
if [ $i -ge $MYSQL_TRIES ]; then
132+
echo "${C_RED}MySQL failed to start. Aborting.${NO_FORMAT}"
133+
echo "Cannot connect to MySQL server. For all available variables, check the documentation at:"
134+
echo " ${C_BLUE}https://github.com/wp-cli/wp-cli-tests?tab=readme-ov-file#the-database-credentials${NO_FORMAT}"
135+
exit 1
97136
fi
98137
done
99138
else
100139
while ! mariadb ${HOST_STRING} --user="${USER}" "${PASSWORD_STRING}" --execute="SHOW DATABASES;" | grep 'information_schema' >/dev/null;
101140
do
102-
echo 'Waiting for database...'
103-
sleep 5
104141
i=$((i+1))
105-
if [ $i -gt 36 ]; then
106-
echo 'Database failed to start. Aborting.'
107-
exit 1
142+
if [ "${MYSQL_TRIES}" -gt 1 ]; then
143+
echo "Waiting for MariaDB(${i}/${MYSQL_TRIES})..."
144+
sleep ${MYSQL_WAIT}
145+
fi
146+
147+
if [ $i -ge $MYSQL_TRIES ]; then
148+
echo "${C_RED}MariaDB failed to start. Aborting.${NO_FORMAT}"
149+
echo "Cannot connect to MariaDB server. For all available variables, check the documentation at:"
150+
echo " ${C_BLUE}https://github.com/wp-cli/wp-cli-tests?tab=readme-ov-file#the-database-credentials${NO_FORMAT}"
151+
exit 1
108152
fi
109153
done
110154
fi
111155

112156
# Prepare the database for running the tests with a MySQL version 8.0 or higher.
113157
install_mysql_db_8_0_plus() {
114-
set -ex
158+
set -ex # print all the commands.
115159
mysql -e "CREATE DATABASE IF NOT EXISTS \`${TEST_DB}\`;" ${HOST_STRING} -u"${USER}" "${PASSWORD_STRING}"
116160
mysql -e "CREATE USER IF NOT EXISTS \`${TEST_USER}\`@'%' IDENTIFIED WITH caching_sha2_password BY '${TEST_PASSWORD}'" ${HOST_STRING} -u"${USER}" "${PASSWORD_STRING}"
117161
mysql -e "GRANT ALL PRIVILEGES ON \`${TEST_DB}\`.* TO '${TEST_USER}'@'%'" ${HOST_STRING} -u"${USER}" "${PASSWORD_STRING}"
118162
mysql -e "GRANT ALL PRIVILEGES ON \`${TEST_DB}_scaffold\`.* TO '${TEST_USER}'@'%'" ${HOST_STRING} -u"${USER}" "${PASSWORD_STRING}"
163+
{ set +ex; } 2> /dev/null # stop printing the commands
119164
}
120165

121166
# Prepare the database for running the tests with a MySQL version lower than 8.0.
122167
install_mysql_db_lower_than_8_0() {
123-
set -ex
168+
set -ex # print all the commands.
124169
mysql -e "CREATE DATABASE IF NOT EXISTS \`${TEST_DB}\`;" ${HOST_STRING} -u"${USER}" "${PASSWORD_STRING}"
125170
mysql -e "GRANT ALL ON \`${TEST_DB}\`.* TO '${TEST_USER}'@'%' IDENTIFIED BY '${TEST_PASSWORD}'" ${HOST_STRING} -u"${USER}" "${PASSWORD_STRING}"
126171
mysql -e "GRANT ALL ON \`${TEST_DB}_scaffold\`.* TO '${TEST_USER}'@'%' IDENTIFIED BY '${TEST_PASSWORD}'" ${HOST_STRING} -u"${USER}" "${PASSWORD_STRING}"
172+
{ set +ex; } 2> /dev/null # stop printing the commands
127173
}
128174

129175
# Prepare the database for running the tests with MariaDB
@@ -142,3 +188,6 @@ elif [ "${MAJOR}" -ge 8 ]; then
142188
else
143189
install_mysql_db_lower_than_8_0
144190
fi
191+
192+
echo "Succesfully prepared the database for running tests."
193+
echo "This command does not have to be run again."

composer.json

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,25 @@
1010
"type": "phpcodesniffer-standard",
1111
"require": {
1212
"php": ">=7.2.24",
13-
"behat/behat": "^3.7",
13+
"behat/behat": "^v3.15.0",
1414
"composer/semver": "^3.4",
1515
"dealerdirect/phpcodesniffer-composer-installer": "^0.4.3 || ^0.5 || ^0.6.2 || ^0.7.1 || ^1.0.0",
1616
"php-parallel-lint/php-console-highlighter": "^1.0",
1717
"php-parallel-lint/php-parallel-lint": "^1.3.1",
18-
"phpcompatibility/php-compatibility": "dev-develop",
19-
"phpstan/extension-installer": "^1.4.3",
20-
"phpstan/phpstan": "^1.12.26",
18+
"phpcompatibility/php-compatibility": "^10.0",
19+
"phpstan/extension-installer": "^1.4",
20+
"phpstan/phpstan": "^1.12.26 || ^2.0",
21+
"phpstan/phpstan-deprecation-rules": "^1.2 || ^2.0",
22+
"phpstan/phpstan-phpunit": "^1.4 || ^2.0",
23+
"phpstan/phpstan-strict-rules": "^1.6 || ^2.0",
24+
"swissspidy/phpstan-no-private": "^0.2.1 || ^1.0",
2125
"szepeviktor/phpstan-wordpress": "^v1.3.5",
2226
"wp-cli/config-command": "^1 || ^2",
2327
"wp-cli/core-command": "^1 || ^2",
2428
"wp-cli/eval-command": "^1 || ^2",
2529
"wp-cli/wp-cli": "^2.12",
26-
"wp-coding-standards/wpcs": "^3",
27-
"yoast/phpunit-polyfills": "^1.0.3 || ^2.0.1"
30+
"wp-coding-standards/wpcs": "dev-develop",
31+
"yoast/phpunit-polyfills": "^1.0 || ^2.0 || ^3.0 || ^4.0"
2832
},
2933
"require-dev": {
3034
"roave/security-advisories": "dev-latest"
@@ -40,7 +44,12 @@
4044
},
4145
"extra": {
4246
"branch-alias": {
43-
"dev-main": "4.0.x-dev"
47+
"dev-main": "5.0.x-dev"
48+
},
49+
"phpstan": {
50+
"includes": [
51+
"extension.neon"
52+
]
4453
},
4554
"readme": {
4655
"sections": [
@@ -59,6 +68,11 @@
5968
"WP_CLI\\Tests\\": "src"
6069
}
6170
},
71+
"autoload-dev": {
72+
"psr-4": {
73+
"WP_CLI\\Tests\\Tests\\": "tests/tests"
74+
}
75+
},
6276
"minimum-stability": "dev",
6377
"prefer-stable": true,
6478
"bin": [

0 commit comments

Comments
 (0)