From dc1517efd563a0b9f9e4be968cc960a6dce59020 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 26 Sep 2026 23:53:56 +0000 Subject: [PATCH] Fix the runtime matrix's nginx cells; run an nginx cell on PRs Since #106, all three nginx cells of the full matrix fail at the settings PATCH with nginx's own "405 Not Allowed": the smoke test sent REST requests to /?rest_route=, and for a directory request nginx's index module only hands GET, HEAD, and POST to index.php, so PATCH, PUT, and DELETE fall through to the static handler. #105's write steps used only GET and POST, so they passed. Sites are not affected: WordPress builds plain-permalink REST URLs as index.php?rest_route=, and pretty permalinks reach index.php through try_files. Send every smoke REST request to index.php?rest_route= (REST_URL), and add nginx-php83-wplatest to the pull-request matrix, which had only Apache cells and so could not catch this before merge. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01KbVuMPN7YTcV27vwsG3LZ2 --- .github/workflows/runtime-matrix.yml | 7 +++++++ CHANGELOG.md | 4 ++++ README.md | 2 ++ scripts/runtime-matrix/smoke.sh | 27 ++++++++++++++++----------- 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/.github/workflows/runtime-matrix.yml b/.github/workflows/runtime-matrix.yml index 2c658d9..cd6f45c 100644 --- a/.github/workflows/runtime-matrix.yml +++ b/.github/workflows/runtime-matrix.yml @@ -43,6 +43,13 @@ jobs: multisite: '1' http_port: '8901' db_port: '33081' + - name: nginx-php83-wplatest + server: nginx + php: '8.3' + wp: latest + db_engine: mysql + http_port: '8897' + db_port: '33077' steps: - name: Checkout diff --git a/CHANGELOG.md b/CHANGELOG.md index ce34bf6..7f352e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- The runtime matrix's nginx cells pass again. The smoke test sent the write routes' `PATCH`, `PUT`, and `DELETE` requests to `/?rest_route=…`, which nginx answers with its own `405` for those methods, so the request never reached WordPress. It now uses `/index.php?rest_route=…`, the form WordPress itself builds for plain permalinks. Sites were not affected. Pull requests now also run an nginx cell, so a server-specific failure shows up before merge. + ## [1.7.0] - 2026-09-26 ### Added diff --git a/README.md b/README.md index 4eb1c98..0c151ea 100644 --- a/README.md +++ b/README.md @@ -338,6 +338,8 @@ The GitHub Actions runtime matrix currently covers: - Nginx + PHP 8.2 + latest WordPress - Nginx + PHP 8.3 + latest WordPress +Pull requests run a four-cell subset: Apache with PHP 7.4 and WordPress 6.4, Apache with PHP 8.3 and latest WordPress (single site and Multisite), and Nginx with PHP 8.3. Pushes to `main` run all ten. + Every cell tests the packaged release, built with `npm run package:release` and including its production `vendor/`, not the source checkout. Each cell: - activates the plugin and checks the front-end render and the read routes; diff --git a/scripts/runtime-matrix/smoke.sh b/scripts/runtime-matrix/smoke.sh index 8eaf30d..d74290a 100755 --- a/scripts/runtime-matrix/smoke.sh +++ b/scripts/runtime-matrix/smoke.sh @@ -11,6 +11,11 @@ MULTISITE="${WP_BIBLIO_MULTISITE:-0}" HTTP_PORT="${WP_BIBLIO_HTTP_PORT:-8899}" DB_PORT="${WP_BIBLIO_DB_PORT:-33069}" SITE_URL="http://127.0.0.1:${HTTP_PORT}" +# REST requests use index.php?rest_route=, the form WordPress itself builds +# for plain permalinks. A bare /?rest_route= only works for GET, HEAD, and +# POST on nginx: its index module hands the directory request to index.php +# for those methods alone, so PATCH, PUT, and DELETE get nginx's own 405. +REST_URL="${SITE_URL}/index.php?rest_route=" WORKDIR="${RUNTIME_ROOT}/${SERVER}-php${PHP_VERSION}-wp${WP_VERSION}-${DB_ENGINE}$([ "$MULTISITE" = "1" ] && printf '%s' '-multisite' || true)" SITE_DIR="${WORKDIR}/site" COMPOSE_FILE="${WORKDIR}/docker-compose.yml" @@ -195,11 +200,11 @@ wait_for_http() { # 404s with rest_no_route. Poll the REST index instead of racing it. wait_for_plugin_routes() { attempt=0 - until curl -fsS "$SITE_URL/?rest_route=/" 2>/dev/null | grep -q 'bibliography\\*/v1'; do + until curl -fsS "${REST_URL}/" 2>/dev/null | grep -q 'bibliography\\*/v1'; do attempt=$((attempt + 1)) if [ "$attempt" -gt 30 ]; then echo "Timed out waiting for bibliography/v1 routes at $SITE_URL" >&2 - curl -sS "$SITE_URL/?rest_route=/" | head -c 2000 >&2 || true + curl -sS "${REST_URL}/" | head -c 2000 >&2 || true echo >&2 exit 1 fi @@ -334,9 +339,9 @@ printf '%s\n' "$POST_ID" > "$ARTIFACT_DIR/post-id.txt" wait_for_plugin_routes capture_http frontend "$SITE_URL/?p=$POST_ID" -capture_http rest-collection "$SITE_URL/?rest_route=/bibliography/v1/posts/$POST_ID/bibliographies" -capture_http rest-text "$SITE_URL/?rest_route=/bibliography/v1/posts/$POST_ID/bibliographies/0&format=text" -capture_http rest-csl-json "$SITE_URL/?rest_route=/bibliography/v1/posts/$POST_ID/bibliographies/0&format=csl-json" +capture_http rest-collection "${REST_URL}/bibliography/v1/posts/$POST_ID/bibliographies" +capture_http rest-text "${REST_URL}/bibliography/v1/posts/$POST_ID/bibliographies/0&format=text" +capture_http rest-csl-json "${REST_URL}/bibliography/v1/posts/$POST_ID/bibliographies/0&format=csl-json" grep -q 'bibliography-builder-entry-text' "$ARTIFACT_RESPONSE_DIR/frontend.body" grep -q '"entryCount":1' "$ARTIFACT_RESPONSE_DIR/rest-collection.body" @@ -378,21 +383,21 @@ fi # until opcache revalidates it (see wait_for_plugin_routes). Until then the # password is ignored and write requests arrive logged out, so wait for it. attempt=0 -until [ "$(curl -sS -o /dev/null -w '%{http_code}' -u "admin:$APP_PASSWORD" "$SITE_URL/?rest_route=/wp/v2/users/me")" = "200" ]; do +until [ "$(curl -sS -o /dev/null -w '%{http_code}' -u "admin:$APP_PASSWORD" "${REST_URL}/wp/v2/users/me")" = "200" ]; do attempt=$((attempt + 1)) if [ "$attempt" -gt 30 ]; then echo "Application password never authenticated at $SITE_URL" >&2 - curl -sS -u "admin:$APP_PASSWORD" "$SITE_URL/?rest_route=/wp/v2/users/me" | head -c 2000 >&2 || true + curl -sS -u "admin:$APP_PASSWORD" "${REST_URL}/wp/v2/users/me" | head -c 2000 >&2 || true echo >&2 exit 1 fi sleep 1 done -CITATIONS_URL="$SITE_URL/?rest_route=/bibliography/v1/posts/$POST_ID/bibliographies/0/citations" +CITATIONS_URL="${REST_URL}/bibliography/v1/posts/$POST_ID/bibliographies/0/citations" NEW_ITEM='{"items":[{"type":"article-journal","title":"Beta Findings","author":[{"family":"Beta","given":"Bea"}],"container-title":"Journal of Smoke Tests","volume":"3","issue":"1","page":"10-20","issued":{"date-parts":[[2021]]}}]}' -rest_call write-read GET "$SITE_URL/?rest_route=/bibliography/v1/posts/$POST_ID/bibliographies" 200 +rest_call write-read GET "${REST_URL}/bibliography/v1/posts/$POST_ID/bibliographies" 200 rest_call write-dry-run POST "$CITATIONS_URL" 200 "$NEW_ITEM" grep -q '"dryRun":true' "$ARTIFACT_RESPONSE_DIR/write-dry-run.body" ETAG=$(header_etag write-dry-run) @@ -406,7 +411,7 @@ rest_call write-commit POST "$CITATIONS_URL&dry_run=false" 200 "$NEW_ITEM" "$ETA grep -q '"dryRun":false' "$ARTIFACT_RESPONSE_DIR/write-commit.body" [ "$(header_etag write-commit)" != "$ETAG" ] -capture_http write-text "$SITE_URL/?rest_route=/bibliography/v1/posts/$POST_ID/bibliographies/0&format=text" +capture_http write-text "${REST_URL}/bibliography/v1/posts/$POST_ID/bibliographies/0&format=text" grep -q 'Beta, Bea' "$ARTIFACT_RESPONSE_DIR/write-text.body" grep -q 'Journal of Smoke Tests 3, no. 1 (2021): 10–20' "$ARTIFACT_RESPONSE_DIR/write-text.body" capture_http write-frontend "$SITE_URL/?p=$POST_ID" @@ -414,7 +419,7 @@ grep -q 'Beta Findings' "$ARTIFACT_RESPONSE_DIR/write-frontend.body" # Block settings and reformatting (Tier 3). The settings PATCH shares its path # with the public GET route, so this also proves the method falls through to it. -BIBLIOGRAPHY_URL="$SITE_URL/?rest_route=/bibliography/v1/posts/$POST_ID/bibliographies/0" +BIBLIOGRAPHY_URL="${REST_URL}/bibliography/v1/posts/$POST_ID/bibliographies/0" rest_call write-settings PATCH "$BIBLIOGRAPHY_URL&dry_run=false" 200 '{"headingText":"Smoke Sources","outputCoins":true}' "$(header_etag write-commit)" grep -q '"headingText":"Smoke Sources"' "$ARTIFACT_RESPONSE_DIR/write-settings.body" rest_call write-reformat POST "$BIBLIOGRAPHY_URL/reformat&dry_run=false" 200 '{"style":"apa-7"}' "$(header_etag write-settings)"