Make WordPress Core

Opened 18 hours ago

Last modified 11 hours ago

#66036 accepted enhancement

Run smoke tests post install and upgrade tests

Reported by: adrianmoldovanwp Owned by: lancewillett
Priority: normal Milestone: 7.2
Component: Build/Test Tools Version:
Severity: normal Keywords: has-patch
Cc: Focuses: tests

Description

The Upgrade Tests CI workflow (.github/workflows/reusable-upgrade-testing.yml) installs an old version of WordPress, upgrades it, and
then runs wp core version. No step in the job further checks the site.

The install tests workflow (.github/workflows/install-testing.yml) is similar, nothing checks the site after the install via cli completes.

Wiring existing checks would be ideal, but the e2e Playwright tests need a more complex setup: node, build, docker, etc, and they cannot run against the already installed/upgraded version directly in the runner.

Explore a light smoke checks option using curl commands against the PHP's built-in server that can be started after install/upgrade.

Change History (7)

This ticket was mentioned in PR #13355 on WordPress/wordpress-develop by @adrianmoldovanwp.


18 hours ago
#1

  • Keywords has-patch added

Trac ticket: https://core.trac.wordpress.org/ticket/66036

Add a light smoke test after the upgrade via cli completed.

Upgrade Tests currently stop at wp core version, which reads a string off the filesystem then report success.

This PR adds one step at the end of the job that moves the site to port 8889, serves it with PHP's built-in server, and makes a few curl requests to validate the site works.

Request Must contain
GET / content="WordPress <version>", matched against wp core version, Hello world!, and </html>
GET /?rest_route=/ a whole JSON body whose name is Upgrade Test
GET /wp-admin/upgrade.php No Update Required and </html>
GET /?p=99999999 HTTP 404
POST /wp-login.php wordpress_logged_in_ in the cookie jar
GET /wp-admin/ id="wpadminbar" and </html>

The step then fails if PHP wrote a fatal error to the server log. That catches a fatal during shutdown, which leaves every response whole.

Each check asserts a marker that a working install must produce, and also </html>.

On a failure the step prints the response body and the server log.

The server starts with -d opcache.jit=disable. Without that it crashes.

The step skips multisite. A network keeps its domain in wp-config.php and in the site and blogs tables, and WordPress strips only :80 and :443 from the host before it matches a network, so a move to another port needs more than an option update.

It sets DISABLE_WP_CRON and WP_HTTP_BLOCK_EXTERNAL. A cron spawn is a request no check asked for, and the update checks on /wp-admin/ leave the runner for api.wordpress.org. WP_Http::block_request() exempts the site's own host, so the site can still talk to itself.

## Why not the existing Playwright tests

They need Node, npm ci, a browser download, npm run build, and a Docker environment. This job has none of those. Their global setup also switches the theme to Twenty Twenty-One and deletes every post, so it would wipe the upgraded site before it tested it.

## Effect on other branches

Branches 6.6 to 7.1 call reusable-upgrade-testing.yml@trunk, so this step runs on their upgrade jobs too. Branches 6.4 and 6.5 call the older upgrade-testing-run.yml and do not change. upgrade-develop-testing.yml calls this workflow too, and runs on any change to src/**.php.

All the markers exist in the source of every branch from 6.6 to trunk, and every caller runs Ubuntu.

## Follow-up

A follow-up should add the same check to install-testing.yml, and moves this block into a composite action that both workflows can call.

## Testing Instructions

The workflow runs on any pull request that touches upgrade-testing.yml or reusable-upgrade-testing.yml, so this one exercises it.

To run locally:

  1. Start a database:
    docker run -d --name wp-smoke-db -e MYSQL_ROOT_PASSWORD=root \
      -e MYSQL_DATABASE=test_db -p 13306:3306 mysql:8.4
    
  2. In an empty directory, install an older version and upgrade it:
    wp core download --version=7.0
    wp config create --dbname=test_db --dbuser=root --dbpass=root --dbhost=127.0.0.1:13306
    wp core install --url=http://localhost/ --title="Upgrade Test" --admin_user=admin \
      --admin_password=password --admin_email=me@example.org --skip-email
    wp core update --minor && wp core update-db
    wp core update && wp core update-db
    
  3. Copy the run: block of the new "Post-upgrade smoke check" step into smoke.sh and run it with RUNNER_TEMP=/tmp SITE_PORT=8889 WP_ADMIN_USER=admin WP_ADMIN_PASSWORD=password bash -e smoke.sh. It prints seven ok lines and exits 0.
  4. Confirm it catches a broken site. Each case below is one file in wp-content/mu-plugins/. Write it, run the script again, then delete it. The WP_CLI guard keeps WP-CLI working, so the break reaches the step over HTTP rather than stopping the first wp command.

| File content | Expected failure |

|

| <?php if ( ! defined( "WP_CLI" ) ) { boom_undefined(); } | ::error::/ returned HTTP 500 |
| <?php add_action( "wp_footer", function () { boom_undefined(); } ); | ::error::/ did not contain: </html> |
| <?php if ( ! defined( "WP_CLI" ) ) { register_shutdown_function( function () { boom_undefined(); } ); } | ::error::the server logged a fatal error |
| <?php add_filter( "template_redirect", function () { if ( is_404() ) { status_header( 200 ); } }, 1 ); | ::error::a missing post returned 200, expected 404 |

The third case leaves every response whole and is caught only by the log check.

  1. Confirm it catches missing content: wp post delete 1 --force, then run the script again. It fails with ::error::/ did not contain: Hello world!.

## Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5
Used for: Drafting the workflow step and tracing the opcache.jit crash mentioned above. I ran the local verification described here and reviewed the result.

#2 @lancewillett
13 hours ago

  • Milestone Awaiting Review7.2
  • Owner set to lancewillett
  • Status newaccepted

#3 @lancewillett
13 hours ago

In 63447:

Build/Test Tools: Smoke test upgraded sites over HTTP.

Upgrade tests previously stopped after checking the WordPress version on disk. Serve upgraded single-site installations with PHP's built-in server and verify the front end, REST API, database upgrade page, 404 handling, login, dashboard, and server log.

Disable cron and external HTTP requests during the check, and disable the runner's PHP JIT for the temporary server. Multisite remains excluded because moving a network to a temporary port requires coordinated configuration and database changes.

Developed in: https://github.com/WordPress/wordpress-develop/pull/13355

Props adrianmoldovanwp.
See #66036.

@lancewillett commented on PR #13355:


13 hours ago
#4

The upgrade workflow smoke check landed in https://core.trac.wordpress.org/changeset/63447. The install workflow follow-up remains as next step.

A follow-up should add the same check to install-testing.yml, and moves this block into a composite action that both workflows can call.

@adimoldovan Do you already have a PR for that?

@adrianmoldovanwp commented on PR #13355:


13 hours ago
#5

The code looks correct and I've left a comment that is not a blocker.

Leaving a note here to pick your brain: this script is 100+ lines of bash code to be read inline into a script that is not syntax highlighted. Moving this to a dedicated .sh script used by the workflow would (pro) make it read like a bash file (and allow to add self-tests) but (con) require the actions/checkout step to pull it in (likely from trunk).

Valid concern, and my plan was to do that in the follow-up that adds this same check in the install workflow.

This ticket was mentioned in PR #13388 on WordPress/wordpress-develop by @adrianmoldovanwp.


12 hours ago
#6

Follow-up to #13355. Moves the upgrade smoke check into a composite action at .github/actions/smoke-check/, with the script in smoke-check.sh, and runs the same check after installation in install-testing.yml.

## Landing order

Land the two commits in the order they appear:

  1. .github/actions/smoke-check/ and the lint changes.
  2. The workflows that reference the action.

The runner resolves every uses: during job setup, before it evaluates if: conditions. Until the action is on trunk, every job in install-testing.yml, upgrade-testing.yml and upgrade-develop-testing.yml fails with Unable to resolve action, including the multisite rows that skip the step. The checks on this pull request fail for that reason until the first commit lands.

## Why @trunk

The 6.9, 7.0 and 7.1 branches call reusable-upgrade-testing.yml@trunk. Trunk's reusable workflow then runs with github.ref set to the release branch, where .github/actions does not exist, so a ./ reference or a plain checkout would fail on every release branch job.

The path filters for .github/actions/smoke-check/** cover push only, for the same reason. A pull request resolves the action from trunk, so such a run would report on the old script.

## Also here

  • shellcheck runs over the scripts under .github/actions. actionlint shellchecks inline run: blocks but does not read composite actions, so moving the script out of the workflow lost that coverage.
  • install-testing.yml gets 15 minutes instead of 10, to cover the added step.
  • The REST check asserts the site URL and the wp/v2 namespace, so it no longer depends on the site title.
  • The script sets show_on_front explicitly, as asked in the review of #13355.

## Testing instructions

Run the script against a local site:

docker run -d --name wp-smoke-db -p 33061:3306 -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=test_db mysql:8.4
mkdir wp && cd wp
wp core download --version=nightly
wp config create --dbname=test_db --dbuser=root --dbpass=root --dbhost=127.0.0.1:33061
wp core install --url=http://localhost/ --title="Install Test" --admin_user=admin \
  --admin_password=password --admin_email=me@example.org --skip-email
RUNNER_TEMP=/tmp WP_ADMIN_USER=admin WP_ADMIN_PASSWORD=password \
  bash /path/to/wordpress-develop/.github/actions/smoke-check/smoke-check.sh

All seven checks print ok and the script exits 0. Note that it rewrites home and siteurl and edits wp-config.php, so the site is only good for the check afterwards.

To see it fail, run it again with a wrong WP_ADMIN_PASSWORD. The login check reports no authentication cookie, the script exits 1, and the server log goes to the job output.

In CI, once the first commit is on trunk: run the Installation Tests workflow from the Actions tab, and push a src/**.php change to a branch to see Upgrade Develop Version Tests run the same step.

## Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5
Used for: Extracting the script into the composite action, the workflow changes, and iterative code review. I reviewed and tested the result.

This ticket was mentioned in PR #13389 on WordPress/wordpress-develop by @adrianmoldovanwp.


11 hours ago
#7

Points the smoke check step at the action on trunk, and drops the checkout that the relative reference needed.

Depends on #13388. Land that first: until the action is on trunk, @trunk does not resolve, and the runner resolves every uses: during job setup.

The 6.9, 7.0 and 7.1 branches call reusable-upgrade-testing.yml@trunk. Trunk's reusable workflow then runs with github.ref set to the release branch, where .github/actions does not exist, so a relative reference fails on every release branch job. Pointing at the action on trunk also matches how those branches already take their CI from trunk.

The path filters for .github/actions/smoke-check/** now cover pushes only. A pull request resolves the action from trunk rather than from the branch, so a pull request run would report on the old script.

## Code scanning

This adds two unpinned-uses alerts, one for each reference. A hash pin is the right rule for a third party action, but it does not fit here:

  • The action lives in this repository. Anyone who can change it on trunk can also edit the workflow file, including a pinned SHA.
  • The reference always resolves from WordPress/wordpress-develop at trunk, never from a fork, so a pull request cannot inject action code.
  • A pin would freeze the release branches on a stale script and need a bump on every change to it.

WordPress/props-bot-action@trunk in props-bot.yml carries the same alert today. Say so if you would rather suppress these two, either with # zizmor: ignore[unpinned-uses] on both lines or with a WordPress/* policy in a repository level zizmor.yml.

## Testing instructions

Once #13388 is on trunk, the Installation Tests and Upgrade Tests checks here resolve the action from trunk and run it. Each single site row prints seven ok lines; multisite rows skip the step.

Before that, both workflows fail at job setup with Unable to resolve action. That is expected, and it is the reason for the two pull requests.

## Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5
Used for: The workflow changes and iterative code review. I reviewed and tested the result.

Note: See TracTickets for help on using tickets.