Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
7918a35
Add Makefile and wp-env-based local dev environment
donnchawp Apr 13, 2026
d0af90b
Add linting commands expected by Makefile
donnchawp Apr 13, 2026
b6a04f9
Fix PHPCS errors in dev seed/unseed scripts
donnchawp Apr 13, 2026
c095922
Add phpcs-changed wrapper scripts and wire up lint commands
donnchawp Apr 13, 2026
f4f1a6e
Auto install @wordpress/env
donnchawp Apr 13, 2026
c6da4b6
cli target opens shell in the cli container
donnchawp Apr 13, 2026
2150427
Fix to `make cli` docs
donnchawp Apr 13, 2026
98ee92d
Remove unused code from this file
donnchawp Apr 13, 2026
857ade5
Fixed link to changelog
donnchawp Apr 13, 2026
4012cd4
Add pre-build and build Make targets for releases
donnchawp Apr 14, 2026
dc02550
Review follow-up: fix dead changelog link, bash portability, sed backups
donnchawp Apr 14, 2026
32db71b
Untrack root package-lock.json
donnchawp Apr 14, 2026
d1c5467
Remove Jetpack release automation in favour of local scripts
donnchawp Apr 14, 2026
b84646e
Drop Jetpack release config and jetpack-changelogger dependency
donnchawp Apr 14, 2026
c1dcafb
Refresh composer.lock content-hash after extras cleanup
donnchawp Apr 14, 2026
7cbde04
Document release pipeline in README
donnchawp Apr 14, 2026
9992300
Release 3.0.4
donnchawp Apr 14, 2026
421c749
Revert these, testing mishap
donnchawp Apr 14, 2026
3af6ab7
Add the version and date to the changelog for convenience
donnchawp Apr 14, 2026
3f7b126
Rename .w.org-assets to w.org-assets and wire into publish.sh
donnchawp Apr 14, 2026
a108d34
Add "trunk" check for publish script
donnchawp Apr 14, 2026
832ccb9
Address PR review: release + lint script hardening
donnchawp Apr 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .wp-env.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"core": null,
"plugins": [ "." ],
"config": {
"WP_DEBUG": true,
"WP_DEBUG_LOG": true,
"WPCACHEHOME": "/var/www/html/wp-content/plugins/wp-super-cache/"
},
"mappings": {
"wp-content/plugins/wp-super-cache": ".",
"wp-content/mu-plugins": "./tests/dev/mu-plugins"
},
"testsEnvironment": false,
"phpVersion": "8.1"
}
50 changes: 50 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
.DEFAULT_GOAL := help

PLUGIN_NAME := wp-super-cache
WP_ENV := COMPOSE_PROJECT_NAME=$(PLUGIN_NAME) npx @wordpress/env
Comment thread
donnchawp marked this conversation as resolved.
Outdated

## Development environment
install: ## Install PHP (Composer) and JS (npm) dependencies
composer install
npm install

up: ## Start WordPress in Docker (http://localhost:8888, admin/password)
$(WP_ENV) start

down: ## Stop the WordPress containers
$(WP_ENV) stop

destroy: ## Remove the WordPress containers and database
$(WP_ENV) destroy

logs: ## Tail the WordPress container logs
$(WP_ENV) logs

cli: ## Open a shell inside the WordPress container
Comment thread
donnchawp marked this conversation as resolved.
Outdated
$(WP_ENV) run cli bash

wp: ## Run an arbitrary wp-cli command, e.g. `make wp CMD="plugin list"`
$(WP_ENV) run cli wp $(CMD)

## Test content
seed: ## Create 100 randomly named posts and 100 pages for cache testing
$(WP_ENV) run cli wp eval-file wp-content/plugins/wp-super-cache/tests/dev/seed.php

unseed: ## Delete content created by `make seed`
$(WP_ENV) run cli wp eval-file wp-content/plugins/wp-super-cache/tests/dev/unseed.php

## Lint
lint: ## Run PHP CodeSniffer
composer lint

lint-all: ## Run PHP CodeSniffer on all files
composer phpcs

lint-fix: ## Auto-fix PHP CodeSniffer issues
composer lint-fix

## Help
help: ## Show this help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-12s\033[0m %s\n", $$1, $$2}'

.PHONY: install up down destroy logs cli wp seed unseed lint lint-all lint-fix help
36 changes: 33 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,35 @@ changelog/ Individual changelog entries (Jetpack Changelogger for

- PHP 7.4+
- [Composer](https://getcomposer.org/)
- Node.js 20+ and Docker (only needed for the Makefile / wp-env workflow)

### Installation
### Quick start with the Makefile

A `Makefile` is provided to spin up a disposable WordPress site in Docker (via [`@wordpress/env`](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-env/)) with the plugin pre-mounted. Run `make help` to see every target.

```bash
make install # composer install + npm install
make up # start WordPress at http://localhost:8888 (admin / password)
make seed # create 100 random posts + 100 random pages for cache testing
# ... hack on the plugin; files are live-mounted into the container ...
make unseed # delete only the content created by `make seed`
make down # stop containers
make destroy # stop and wipe the database
```

Other useful targets:

| Target | Purpose |
|--------|---------|
| `make cli` | Open a shell inside the WordPress container |
Comment thread
donnchawp marked this conversation as resolved.
Outdated
| `make wp CMD="super-cache status"` | Run an arbitrary `wp-cli` command |
| `make logs` | Tail the WordPress container logs |
| `make lint` / `make lint-fix` | Run / auto-fix PHPCS on changed PHP files |
| `make lint-all` | Run PHPCS on the full codebase |

The seed script tags every item it creates with `_wpsc_seed=1` post meta, so `make unseed` only removes content it generated — it will not touch posts or pages you created by hand.

### Manual installation

```bash
composer install
Expand All @@ -59,8 +86,11 @@ composer test-coverage
### Linting

```bash
# PHPCS (WordPress/Jetpack coding standards)
vendor/bin/phpcs
# Changed PHP files only (matches CI)
make lint

# Full-tree PHPCS (WordPress/Jetpack coding standards)
make lint-all
```

### Static analysis
Expand Down
12 changes: 12 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@
]
},
"scripts": {
"phpcs": [
"./tools/phpcs-wrapper.sh --standard=.phpcs.xml.dist ."
],
"phpcbf": [
"./tools/phpcbf-wrapper.sh --standard=.phpcs.xml.dist ."
],
"lint": [
"./tools/run-phpcs-changed.sh"
],
"lint-fix": [
"./tools/run-phpcbf-changed.sh"
],
"phpunit": [
"phpunit-select-config phpunit.#.xml.dist --colors=always"
],
Expand Down
56 changes: 56 additions & 0 deletions tests/dev/mu-plugins/wpsc-loopback-fix.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php
/**
* Plugin Name: WP Super Cache dev loopback fix
*
* The @wordpress/env container exposes WordPress on port 80 internally but
* is mapped to http://localhost:8888 on the host. WordPress therefore stores
* its site URL as http://localhost:8888, and any PHP code doing a loopback
* HTTP request (WP Super Cache preloader, WP-Cron, REST self-calls, etc.)
* will try to reach :8888 inside the container — where nothing is listening.
*
* This mu-plugin intercepts outgoing HTTP requests aimed at the host-side
* URL and retries them against the docker-compose service hostname, so
* loopback works the same way in Docker as it does on a normal host — both
* when PHP runs inside the `wordpress` container (cron, preloader) and
* when wp-cli triggers loopback from the separate `cli` container.
*
* @package WP_Super_Cache
*/

if ( ! defined( 'ABSPATH' ) ) {
return;
}

add_filter(
'pre_http_request',
function ( $preempt, $args, $url ) {
static $rewriting = false;

if ( $rewriting || ! is_string( $url ) ) {
return $preempt;
}

$host_url = 'http://localhost:8888';
if ( strncasecmp( $url, $host_url, strlen( $host_url ) ) !== 0 ) {
return $preempt;
}

// Route the request to the internal docker service (port 80) but
// preserve the Host header so WordPress does not canonical-redirect
// back to http://localhost:8888.
$rewritten = 'http://wordpress' . substr( $url, strlen( $host_url ) );

if ( ! isset( $args['headers'] ) || ! is_array( $args['headers'] ) ) {
$args['headers'] = array();
}
$args['headers']['Host'] = 'localhost:8888';
Comment thread
donnchawp marked this conversation as resolved.

Comment thread
donnchawp marked this conversation as resolved.
$rewriting = true;
$response = wp_remote_request( $rewritten, $args );
$rewriting = false;

return $response;
},
10,
3
);
61 changes: 61 additions & 0 deletions tests/dev/seed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php
/**
* Seed a local development site with randomly-named posts and pages so that
* caching behavior (preload, garbage collection, mod_rewrite) can be exercised
* against a realistic volume of content.
*
* Intended to be executed via `wp eval-file` inside the `make up` environment.
* Every item is tagged with post_meta `_wpsc_seed=1` so `tests/dev/unseed.php`
* can remove the seeded content without touching anything else.
*
* @package WP_Super_Cache
*/

if ( ! defined( 'WP_CLI' ) || ! WP_CLI ) {
exit( 1 );
}

$count = 100;

function wpsc_seed_random_title( $prefix ) {
$words = array( 'alpha', 'bravo', 'charlie', 'delta', 'echo', 'foxtrot', 'golf', 'hotel', 'india', 'juliet', 'kilo', 'lima', 'mike', 'november', 'oscar', 'papa', 'quebec', 'romeo', 'sierra', 'tango', 'uniform', 'victor', 'whiskey', 'xray', 'yankee', 'zulu' );
shuffle( $words );
return $prefix . ': ' . ucfirst( $words[0] ) . ' ' . ucfirst( $words[1] ) . ' ' . substr( md5( (string) wp_rand() ), 0, 6 );
}

function wpsc_seed_random_body() {
$body = '';
for ( $p = 0; $p < 5; $p++ ) {
$body .= '<p>' . str_repeat( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. ', wp_rand( 3, 8 ) ) . "</p>\n\n";
}
return $body;
}

$created = array(
'post' => 0,
'page' => 0,
);

foreach ( array( 'post', 'page' ) as $content_type ) {
for ( $i = 0; $i < $count; $i++ ) {
$inserted_post_id = wp_insert_post(
array(
'post_title' => wpsc_seed_random_title( ucfirst( $content_type ) ),
'post_content' => wpsc_seed_random_body(),
'post_status' => 'publish',
'post_type' => $content_type,
),
true
);

if ( is_wp_error( $inserted_post_id ) ) {
WP_CLI::warning( sprintf( 'Failed to insert %s: %s', $content_type, $inserted_post_id->get_error_message() ) );
continue;
}

update_post_meta( $inserted_post_id, '_wpsc_seed', 1 );
++$created[ $content_type ];
}
}

WP_CLI::success( sprintf( 'Seeded %d posts and %d pages.', $created['post'], $created['page'] ) );
31 changes: 31 additions & 0 deletions tests/dev/unseed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php
/**
* Remove content created by tests/dev/seed.php. Safe to run repeatedly —
* deletes only posts/pages that carry the `_wpsc_seed=1` meta key.
*
* @package WP_Super_Cache
*/

if ( ! defined( 'WP_CLI' ) || ! WP_CLI ) {
exit( 1 );
}

$ids = get_posts(
array(
'post_type' => array( 'post', 'page' ),
'post_status' => 'any',
'posts_per_page' => -1,
'fields' => 'ids',
'meta_key' => '_wpsc_seed',
'meta_value' => 1,
)
);

$deleted = 0;
foreach ( $ids as $post_to_delete_id ) {
if ( wp_delete_post( $post_to_delete_id, true ) ) {
++$deleted;
}
}

WP_CLI::success( sprintf( 'Deleted %d seeded posts/pages.', $deleted ) );
7 changes: 7 additions & 0 deletions tools/phpcbf-wrapper.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash

set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"

exec php -d error_reporting='E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED' "$ROOT_DIR/vendor/bin/phpcbf" "$@"
7 changes: 7 additions & 0 deletions tools/phpcs-wrapper.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash

set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"

exec php -d error_reporting='E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED' "$ROOT_DIR/vendor/bin/phpcs" "$@"
45 changes: 45 additions & 0 deletions tools/run-phpcbf-changed.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env bash

set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PHPCBF_WRAPPER="$ROOT_DIR/tools/phpcbf-wrapper.sh"
STANDARD="$ROOT_DIR/.phpcs.xml.dist"

dedupe_files() {
awk 'NF && !seen[$0]++'
}

collect_files() {
local cmd=("$@")
"${cmd[@]}" | dedupe_files
}

Comment thread
donnchawp marked this conversation as resolved.
Outdated
base_ref=""
if git rev-parse --verify origin/trunk >/dev/null 2>&1; then
base_ref="origin/trunk"
elif git rev-parse --verify trunk >/dev/null 2>&1; then
base_ref="trunk"
fi

files="$(
{
if [ -n "$base_ref" ]; then
merge_base="$(git merge-base HEAD "$base_ref")"
git diff --name-only --diff-filter=ACMR "$merge_base"...HEAD -- '*.php'
fi
git diff --cached --name-only --diff-filter=ACMR -- '*.php'
git diff --name-only --diff-filter=ACMR -- '*.php'
git ls-files --others --exclude-standard -- '*.php'
} | dedupe_files
)"

if [ -z "$files" ]; then
echo "No changed PHP files found, skipping PHPCBF."
exit 0
fi

mapfile -t file_list <<<"$files"

echo "Running PHPCBF on changed PHP files..."
"$PHPCBF_WRAPPER" --standard="$STANDARD" "${file_list[@]}"
Loading
Loading